MDL-63262 badges: Add badgr.io

Upgrade the support for Open Badges 2 to support a real open badges 2 backpack.
Moodle can only talk to one backpack at a time, so after switching backpacks, users
will have to manually disconnect and then reconnect their backpack to the new one.

This commit combines several changes:
* Split classes into badges namespace
* Publish expiry data with a badge
* Publish badge alignments in badges exported to the backpack.
* Export the criteria for a badge to the backpack.
This commit is contained in:
Damyon Wiese
2019-05-03 15:00:00 +08:00
parent 09cbe51999
commit aae219acce
58 changed files with 4950 additions and 1643 deletions
+26 -3
View File
@@ -57,9 +57,11 @@ if (($hassiteconfig || has_any_capability(array(
new lang_string('badgesalt_desc', 'badges'),
'badges' . $SITE->timecreated, PARAM_ALPHANUM));
$globalsettings->add(new admin_setting_configcheckbox('badges_allowexternalbackpack',
new lang_string('allowexternalbackpack', 'badges'),
new lang_string('allowexternalbackpack_desc', 'badges'), 1));
$backpacks = badges_get_site_backpacks();
$choices = array();
foreach ($backpacks as $backpack) {
$choices[$backpack->id] = $backpack->backpackweburl;
}
$globalsettings->add(new admin_setting_configcheckbox('badges_allowcoursebadges',
new lang_string('allowcoursebadges', 'badges'),
@@ -91,4 +93,25 @@ if (($hassiteconfig || has_any_capability(array(
array('moodle/badges:createbadge'), empty($CFG->enablebadges)
)
);
$backpacksettings = new admin_settingpage('backpacksettings', new lang_string('backpacksettings', 'badges'),
array('moodle/badges:manageglobalsettings'), empty($CFG->enablebadges));
$backpacksettings->add(new admin_setting_configcheckbox('badges_allowexternalbackpack',
new lang_string('allowexternalbackpack', 'badges'),
new lang_string('allowexternalbackpack_desc', 'badges'), 1));
$backpacksettings->add(new admin_setting_configselect('badges_site_backpack',
new lang_string('sitebackpack', 'badges'),
new lang_string('sitebackpack_help', 'badges'),
1, $choices));
$ADMIN->add('badges', $backpacksettings);
$ADMIN->add('badges',
new admin_externalpage('managebackpacks',
new lang_string('managebackpacks', 'badges'),
new moodle_url('/badges/backpacks.php'),
array('moodle/badges:manageglobalsettings'), empty($CFG->enablebadges) || empty($CFG->badges_allowexternalbackpack)
)
);
}
+1 -1
View File
@@ -67,7 +67,7 @@ if ($copy) {
$cloneid = $badge->make_clone();
// If a user can edit badge details, they will be redirected to the edit page.
if (has_capability('moodle/badges:configuredetails', $context)) {
redirect(new moodle_url('/badges/edit.php', array('id' => $cloneid, 'action' => 'details')));
redirect(new moodle_url('/badges/edit.php', array('id' => $cloneid, 'action' => 'badge')));
}
redirect(new moodle_url('/badges/overview.php', array('id' => $cloneid)));
}
+1 -1
View File
@@ -99,7 +99,7 @@ if ($alignmentid || $action == 'add' || $action == 'edit') {
}
$alignments = $badge->get_alignments();
if (count($alignments) > 0) {
$renderrelated = new badge_alignments($alignments, $badgeid);
$renderrelated = new \core_badges\output\badge_alignments($alignments, $badgeid);
echo $output->render($renderrelated);
} else {
echo $output->notification(get_string('noalignment', 'badges'));
+97
View File
@@ -0,0 +1,97 @@
<?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/>.
/**
* Display details of an issued badge with criteria and evidence
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../config.php');
require_once($CFG->libdir . '/badgeslib.php');
if (badges_open_badges_backpack_api() != OPEN_BADGES_V2) {
throw new coding_exception('No backpacks support Open Badges V2.');
}
require_login();
$id = required_param('hash', PARAM_ALPHANUM);
$PAGE->set_url('/badges/backpack-add.php', array('hash' => $id));
$PAGE->set_context(context_system::instance());
$output = $PAGE->get_renderer('core', 'badges');
$issuedbadge = new \core_badges\output\issued_badge($id);
if (!empty($issuedbadge->recipient->id)) {
// The flow for issuing a badge is:
// * Create issuer
// * Create badge
// * Create assertion (Award the badge!)
// Get the backpack.
$badgeid = $issuedbadge->badgeid;
$badge = new badge($badgeid);
$backpack = $DB->get_record('badge_backpack', array('userid' => $USER->id));
$sitebackpack = badges_get_site_backpack(0, $backpack->backpackurl);
$assertion = new core_badges_assertion($id, $backpack->apiversion);
$api = new \core_badges\backpack_api($sitebackpack);
$api->authenticate();
// Create issuer.
$issuer = $assertion->get_issuer();
if (!($issuerentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ISSUER, $issuer['email']))) {
$response = $api->put_issuer($issuer);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$issuerentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ISSUER, $issuer['email'], $issuerentityid);
}
// Create badge.
$badge = $assertion->get_badge_class(false);
$badgeid = $assertion->get_badge_id();
if (!($badgeentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid))) {
$response = $api->put_badgeclass($issuerentityid, $badge);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$badgeentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid, $badgeentityid);
}
// Create assertion (Award the badge!).
$assertiondata = $assertion->get_badge_assertion(false, false);
$assertionid = $assertion->get_assertion_hash();
if (!($assertionentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid))) {
$response = $api->put_badgeclass_assertion($badgeentityid, $assertiondata);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$assertionentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid, $assertionentityid);
$response = ['success' => 'addedtobackpack'];
} else {
$response = ['warning' => 'existsinbackpack'];
}
redirect(new moodle_url('/badges/mybadges.php', $response));
} else {
redirect(new moodle_url('/badges/mybadges.php'));
}
+15 -10
View File
@@ -24,11 +24,10 @@
*/
require_once(__DIR__ . '/../config.php');
require_once($CFG->libdir . '/badgeslib.php');
require_once(__DIR__ . '/lib/backpacklib.php');
$data = optional_param('data', '', PARAM_RAW);
require_login();
$PAGE->set_url('/badges/openbackpackemailverify.php');
$PAGE->set_url('/badges/backpackemailverify.php');
$PAGE->set_context(context_user::instance($USER->id));
$redirect = '/badges/mybackpack.php';
@@ -37,17 +36,21 @@ $storedsecret = get_user_preferences('badges_email_verify_secret');
if (!is_null($storedsecret)) {
if ($data === $storedsecret) {
$storedemail = get_user_preferences('badges_email_verify_address');
$backpackid = get_user_preferences('badges_email_verify_backpackid');
$password = get_user_preferences('badges_email_verify_password');
$backpack = badges_get_site_backpack($backpackid);
$data = new stdClass();
$data->backpackurl = BADGE_BACKPACKURL;
$data->backpackurl = $backpack->backpackapiurl;
$data->apiversion = $backpack->apiversion;
$data->email = $storedemail;
$bp = new OpenBadgesBackpackHandler($data);
$data->password = $password;
$bp = new \core_badges\backpack_api($backpack, $data);
// Make sure we have all the required information before trying to save the connection.
$backpackuser = $bp->curl_request('user');
if (isset($backpackuser->status) && $backpackuser->status === 'okay' && isset($backpackuser->userId)) {
$backpackuid = $backpackuser->userId;
} else {
$backpackuid = $bp->authenticate();
if (empty($backpackuid) || !empty($backpackuid->error)) {
redirect(new moodle_url($redirect), get_string('backpackconnectionunexpectedresult', 'badges'),
null, \core\output\notification::NOTIFY_ERROR);
}
@@ -55,10 +58,12 @@ if (!is_null($storedsecret)) {
$obj = new stdClass();
$obj->userid = $USER->id;
$obj->email = $data->email;
$obj->backpackurl = $data->backpackurl;
$obj->backpackurl = $backpack->backpackapiurl;
$obj->apiversion = $backpack->apiversion;
$obj->backpackuid = $backpackuid;
$obj->autosync = 0;
$obj->password = '';
$obj->password = $password;
$DB->insert_record('badge_backpack', $obj);
// Remove the verification vars and redirect to the mypackpack page.
+78
View File
@@ -0,0 +1,78 @@
<?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/>.
/**
* Display a list of badge backpacks for the site.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../config.php');
require_once($CFG->libdir . '/badgeslib.php');
$context = context_system::instance();
$PAGE->set_context($context);
require_login(0, false);
require_capability('moodle/badges:manageglobalsettings', $context);
// There should be an admin setting to completely turn off badges.
$output = $PAGE->get_renderer('core', 'badges');
$id = optional_param('id', 0, PARAM_INT);
$action = optional_param('action', '', PARAM_ALPHA);
$PAGE->set_pagelayout('admin');
$url = new moodle_url('/badges/backpacks.php');
if (empty($CFG->badges_allowexternalbackpack)) {
redirect($CFG->wwwroot);
}
$PAGE->set_url($url);
$PAGE->set_title(get_string('managebackpacks', 'badges'));
$PAGE->set_heading($SITE->fullname);
if ($action == 'edit') {
$backpack = null;
if (!empty($id)) {
$backpack = badges_get_site_backpack($id);
}
$form = new \core_badges\form\external_backpack(null, ['externalbackpack' => $backpack]);
if ($form->is_cancelled()) {
redirect($url);
} else if ($data = $form->get_data()) {
require_sesskey();
if (!empty($data->id)) {
badges_update_site_backpack($data->id, $data);
} else {
badges_create_site_backpack($data);
}
redirect($url);
}
echo $OUTPUT->header();
echo $output->heading(get_string('managebackpacks', 'badges'));
$form->display();
} else {
echo $OUTPUT->header();
echo $output->heading(get_string('managebackpacks', 'badges'));
$page = new \core_badges\output\external_backpacks_page($url);
echo $output->render($page);
}
echo $OUTPUT->footer();
+1 -1
View File
@@ -38,7 +38,7 @@ $PAGE->set_url('/badges/badge.php', array('hash' => $id));
$PAGE->set_pagelayout('base');
$PAGE->set_title(get_string('issuedbadge', 'badges'));
$badge = new issued_badge($id);
$badge = new \core_badges\output\issued_badge($id);
if (!empty($badge->recipient->id)) {
if ($bake && ($badge->recipient->id == $USER->id)) {
$name = str_replace(' ', '_', $badge->badgeclass['name']) . '.png';
+1 -1
View File
@@ -108,7 +108,7 @@ if ($badge->status != BADGE_STATUS_INACTIVE) {
if ($item->targetcode) {
$alignment['targetCode'] = $item->targetcode;
}
$json['alignment'][] = $alignment;
$json['alignments'][] = $alignment;
}
}
} else if ($action == 0) {
+82 -23
View File
@@ -87,12 +87,40 @@ class core_badges_assertion {
$this->_obversion = $obversion;
}
/**
* Get the local id for this badge.
*
* @return int
*/
public function get_badge_id() {
$badgeid = 0;
if ($this->_data) {
$badgeid = $this->_data->id;
}
return $badgeid;
}
/**
* Get the local id for this badge assertion.
*
* @return string
*/
public function get_assertion_hash() {
$hash = '';
if ($this->_data) {
$hash = $this->_data->uniquehash;
}
return $hash;
}
/**
* Get badge assertion.
*
* @param boolean $issued Include the nested badge issued information.
* @param boolean $usesalt Hash the identity and include the salt information for the hash.
* @return array Badge assertion.
*/
public function get_badge_assertion() {
public function get_badge_assertion($issued = true, $usesalt = true) {
global $CFG;
$assertion = array();
if ($this->_data) {
@@ -104,17 +132,27 @@ class core_badges_assertion {
// Required.
$assertion['uid'] = $hash;
$assertion['recipient'] = array();
$assertion['recipient']['identity'] = 'sha256$' . hash('sha256', $email . $CFG->badges_badgesalt);
if ($usesalt) {
$assertion['recipient']['identity'] = 'sha256$' . hash('sha256', $email . $CFG->badges_badgesalt);
} else {
$assertion['recipient']['identity'] = $email;
}
$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);
if ($usesalt) {
$assertion['recipient']['salt'] = $CFG->badges_badgesalt;
}
if ($issued) {
$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;
if ($issued) {
$assertion['evidence'] = $this->_url->out(false); // Currently issued badge URL.
}
// Optional.
$assertion['evidence'] = $this->_url->out(false); // Currently issued badge URL.
if (!empty($this->_data->dateexpire)) {
$assertion['expires'] = $this->_data->dateexpire;
}
@@ -126,25 +164,42 @@ class core_badges_assertion {
/**
* Get badge class information.
*
* @param boolean $issued Include the nested badge issuer information.
* @return array Badge Class information.
*/
public function get_badge_class() {
$class = array();
public function get_badge_class($issued = true) {
$class = [];
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);
$storage = get_file_storage();
$imagefile = $storage->get_file($context->id, 'badges', 'badgeimage', $this->_data->id, '/', 'f1.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.');
}
}
$class['image'] = 'data:image/png;base64,' . $imagedata;
$class['criteria'] = $this->_url->out(false); // Currently issued badge URL.
$class['issuer'] = $issuerurl->out(false);
if ($issued) {
$issuerurl = new moodle_url('/badges/assertion.php', array('b' => $this->_data->uniquehash, 'action' => 0));
$class['issuer'] = $issuerurl->out(false);
}
$this->embed_data_badge_version2($class, OPEN_BADGES_V2_TYPE_BADGE);
if (!$issued) {
unset($class['issuer']);
}
}
return $class;
}
@@ -155,6 +210,7 @@ class core_badges_assertion {
* @return array Issuer information.
*/
public function get_issuer() {
global $CFG;
$issuer = array();
if ($this->_data) {
// Required.
@@ -163,6 +219,8 @@ class core_badges_assertion {
// Optional.
if (!empty($this->_data->issuercontact)) {
$issuer['email'] = $this->_data->issuercontact;
} else {
$issuer['email'] = $CFG->badges_defaultissuercontact;
}
}
$this->embed_data_badge_version2($issuer, OPEN_BADGES_V2_TYPE_ISSUER);
@@ -284,7 +342,6 @@ class core_badges_assertion {
}
unset($json['uid']);
}
// For Badge.
if ($type == OPEN_BADGES_V2_TYPE_BADGE) {
$json['@context'] = OPEN_BADGES_V2_CONTEXT;
@@ -302,23 +359,25 @@ class core_badges_assertion {
$json['endorsement'] = $endorsementurl->out(false);
}
if ($alignments = $this->get_alignments()) {
$json['alignment'] = $alignments;
$json['alignments'] = $alignments;
}
if ($this->_data->imageauthorname ||
$this->_data->imageauthoremail ||
$this->_data->imageauthorurl ||
$this->_data->imagecaption) {
$urlimage = moodle_url::make_pluginfile_url($context->id,
'badges', 'badgeimage', $this->_data->id, '/', 'f1')->out(false);
$json['image'] = array();
$json['image']['id'] = $urlimage;
if ($this->_data->imageauthorname || $this->_data->imageauthoremail || $this->_data->imageauthorurl) {
$authorimage = new moodle_url('/badges/image_author_json.php', array('id' => $this->_data->id));
$json['image']['author'] = $authorimage->out(false);
}
if ($this->_data->imagecaption) {
$json['image']['caption'] = $this->_data->imagecaption;
$storage = get_file_storage();
$imagefile = $storage->get_file($context->id, 'badges', 'badgeimage', $this->_data->id, '/', 'f1.png');
if ($imagefile) {
$imagedata = base64_encode($imagefile->get_content());
} else {
// The file might not exist in unit tests.
if (defined('PHPUNIT_TEST') && PHPUNIT_TEST) {
$imagedata = '';
} else {
throw new coding_exception('Image file does not exist.');
}
}
$json['image'] = 'data:image/png;base64,' . $imagedata;
}
}
+678
View File
@@ -0,0 +1,678 @@
<?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/>.
/**
* Communicate with backpacks.
*
* @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 <[email protected]>
*/
namespace core_badges;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/filelib.php');
use cache;
use coding_exception;
use core_badges\external\assertion_exporter;
use core_badges\external\collection_exporter;
use core_badges\external\issuer_exporter;
use core_badges\external\badgeclass_exporter;
use curl;
use stdClass;
define('BADGE_ACCESS_TOKEN', 'access');
define('BADGE_USER_ID_TOKEN', 'user_id');
define('BADGE_BACKPACK_ID_TOKEN', 'backpack_id');
define('BADGE_REFRESH_TOKEN', 'refresh');
define('BADGE_EXPIRES_TOKEN', 'expires');
/**
* Class for communicating with backpacks.
*/
class backpack_api {
/** @var string The email address of the issuer or the backpack owner. */
private $email;
/** @var string The base url used for api requests to this backpack. */
private $backpackapiurl;
/** @var integer The backpack api version to use. */
private $backpackapiversion;
/** @var string The password to authenticate requests. */
private $password;
/** @var boolean User or site api requests. */
private $isuserbackpack;
/** @var integer The id of the backpack we are talking to. */
private $backpackid;
/** @var \backpack_api_mapping[] List of apis for the user or site using api version 1 or 2. */
private $mappings = [];
/**
* Create a wrapper to communicate with the backpack.
*
* The resulting class can only do either site backpack communication or
* user backpack communication.
*
* @param stdClass $sitebackpack The site backpack record
* @param mixed $userbackpack Optional - if passed it represents the users backpack.
*/
public function __construct($sitebackpack, $userbackpack = false) {
global $CFG;
$admin = get_admin();
$this->backpackapiurl = $sitebackpack->backpackapiurl;
$this->backpackapiurl = $sitebackpack->backpackapiurl;
$this->backpackapiversion = $sitebackpack->apiversion;
$this->password = $sitebackpack->password;
$this->email = !empty($CFG->badges_defaultissuercontact) ? $CFG->badges_defaultissuercontact : $admin->email;
$this->isuserbackpack = false;
$this->backpackid = $sitebackpack->id;
if (!empty($userbackpack)) {
$this->isuserbackpack = true;
$this->backpackapiurl = $userbackpack->backpackurl;
$this->backpackapiversion = $userbackpack->apiversion;
$this->password = $userbackpack->password;
$this->email = $userbackpack->email;
}
$this->define_mappings();
}
/**
* Define the mappings supported by this usage and api version.
*/
private function define_mappings() {
if ($this->backpackapiversion == OPEN_BADGES_V2) {
if ($this->isuserbackpack) {
$mapping = [];
$mapping[] = [
'collections', // Action.
'[URL]/backpack/collections', // URL
[], // Post params.
'', // Request exporter.
'core_badges\external\collection_exporter', // Response exporter.
true, // Multiple.
'get', // Method.
true, // JSON Encoded.
true // Auth required.
];
$mapping[] = [
'user', // Action.
'[SCHEME]://[HOST]/o/token', // URL
['username' => '[EMAIL]', 'password' => '[PASSWORD]'], // Post params.
'', // Request exporter.
'oauth_token_response', // Response exporter.
false, // Multiple.
'post', // Method.
false, // JSON Encoded.
false, // Auth required.
];
$mapping[] = [
'issuer', // Action.
'[URL]/issuers/[PARAM2]', // URL
[], // Post params.
'', // Request exporter.
'core_badges\external\issuer_exporter', // Response exporter.
false, // Multiple.
'get', // Method.
true, // JSON Encoded.
true // Auth required.
];
$mapping[] = [
'badgeclass', // Action.
'[URL]/badgeclasses/[PARAM2]', // URL
[], // Post params.
'', // Request exporter.
'core_badges\external\badgeclass_exporter', // Response exporter.
false, // Multiple.
'get', // Method.
true, // JSON Encoded.
true // Auth required.
];
$mapping[] = [
'assertion', // Action.
'[URL]/backpack/assertions/[PARAM2]', // URL
[], // Post params.
'', // Request exporter.
'core_badges\external\assertion_exporter', // Response exporter.
false, // Multiple.
'get', // Method.
true, // JSON Encoded.
true // Auth required.
];
$mapping[] = [
'badges', // Action.
'[URL]/backpack/collections/[PARAM1]', // URL
[], // Post params.
'', // Request exporter.
'core_badges\external\collection_exporter', // Response exporter.
true, // Multiple.
'get', // Method.
true, // JSON Encoded.
true // Auth required.
];
foreach ($mapping as $map) {
$map[] = true; // User api function.
$map[] = OPEN_BADGES_V2; // V2 function.
$this->mappings[] = new backpack_api_mapping(...$map);
}
} else {
$mapping = [];
$mapping[] = [
'user', // Action.
'[SCHEME]://[HOST]/o/token', // URL
['username' => '[EMAIL]', 'password' => '[PASSWORD]'], // Post params.
'', // Request exporter.
'oauth_token_response', // Response exporter.
false, // Multiple.
'post', // Method.
false, // JSON Encoded.
false // Auth required.
];
$mapping[] = [
'issuers', // Action.
'[URL]/issuers', // URL
'[PARAM]', // Post params.
'core_badges\external\issuer_exporter', // Request exporter.
'core_badges\external\issuer_exporter', // Response exporter.
false, // Multiple.
'post', // Method.
true, // JSON Encoded.
true // Auth required.
];
$mapping[] = [
'badgeclasses', // Action.
'[URL]/issuers/[PARAM2]/badgeclasses', // URL
'[PARAM]', // Post params.
'core_badges\external\badgeclass_exporter', // Request exporter.
'core_badges\external\badgeclass_exporter', // Response exporter.
false, // Multiple.
'post', // Method.
true, // JSON Encoded.
true // Auth required.
];
$mapping[] = [
'assertions', // Action.
'[URL]/badgeclasses/[PARAM2]/assertions', // URL
'[PARAM]', // Post params.
'core_badges\external\assertion_exporter', // Request exporter.
'core_badges\external\assertion_exporter', // Response exporter.
false, // Multiple.
'post', // Method.
true, // JSON Encoded.
true // Auth required.
];
foreach ($mapping as $map) {
$map[] = false; // Site api function.
$map[] = OPEN_BADGES_V2; // V2 function.
$this->mappings[] = new backpack_api_mapping(...$map);
}
}
} else {
if ($this->isuserbackpack) {
$mapping = [];
$mapping[] = [
'user', // Action.
'[URL]/displayer/convert/email', // URL
['email' => '[EMAIL]'], // Post params.
'', // Request exporter.
'convert_email_response', // Response exporter.
false, // Multiple.
'post', // Method.
false, // JSON Encoded.
false // Auth required.
];
$mapping[] = [
'groups', // Action.
'[URL]/displayer/[PARAM1]/groups.json', // URL
[], // Post params.
'', // Request exporter.
'', // Response exporter.
false, // Multiple.
'get', // Method.
true, // JSON Encoded.
true // Auth required.
];
$mapping[] = [
'badges', // Action.
'[URL]/displayer/[PARAM2]/group/[PARAM1].json', // URL
[], // Post params.
'', // Request exporter.
'', // Response exporter.
false, // Multiple.
'get', // Method.
true, // JSON Encoded.
true // Auth required.
];
foreach ($mapping as $map) {
$map[] = true; // User api function.
$map[] = OPEN_BADGES_V1; // V1 function.
$this->mappings[] = new backpack_api_mapping(...$map);
}
} else {
$mapping = [];
$mapping[] = [
'user', // Action.
'[URL]/displayer/convert/email', // URL
['email' => '[EMAIL]'], // Post params.
'', // Request exporter.
'convert_email_response', // Response exporter.
false, // Multiple.
'post', // Method.
false, // JSON Encoded.
false // Auth required.
];
foreach ($mapping as $map) {
$map[] = false; // Site api function.
$map[] = OPEN_BADGES_V1; // V1 function.
$this->mappings[] = new backpack_api_mapping(...$map);
}
}
}
}
/**
* Make an api request
*
* @param string $action The api function.
* @param string $collection An api parameter
* @param string $entityid An api parameter
* @param string $postdata The body of the api request.
* @return mixed
*/
private function curl_request($action, $collection = null, $entityid = null, $postdata = null) {
global $CFG, $SESSION;
$curl = new curl();
$authrequired = false;
if ($this->backpackapiversion == OPEN_BADGES_V1) {
$useridkey = $this->get_token_key(BADGE_USER_ID_TOKEN);
if (isset($SESSION->$useridkey)) {
if ($collection == null) {
$collection = $SESSION->$useridkey;
} else {
$entityid = $SESSION->$useridkey;
}
}
}
foreach ($this->mappings as $mapping) {
if ($mapping->is_match($action)) {
return $mapping->request(
$this->backpackapiurl,
$collection,
$entityid,
$this->email,
$this->password,
$postdata,
$this->backpackid
);
}
}
throw new coding_exception('Unknown request');
}
/**
* Get the id to use for requests with this api.
*
* @return integer
*/
private function get_auth_user_id() {
global $USER;
if ($this->isuserbackpack) {
return $USER->id;
} else {
// The access tokens for the system backpack are shared.
return -1;
}
}
/**
* Get the name of the key to store this access token type.
*
* @param string $type
* @return string
*/
private function get_token_key($type) {
// This should be removed when everything has a mapping.
$prefix = 'badges_';
if ($this->isuserbackpack) {
$prefix .= 'user_backpack_';
} else {
$prefix .= 'site_backpack_';
}
$prefix .= $type . '_token';
return $prefix;
}
/**
* Normalise the return from a missing user request.
*
* @param string $status
* @return mixed
*/
private function check_status($status) {
// V1 ONLY.
switch($status) {
case "missing":
$response = array(
'status' => $status,
'message' => get_string('error:nosuchuser', 'badges')
);
return $response;
}
return false;
}
/**
* Make an api request to get an assertion
*
* @param string $entityid The id of the assertion.
* @return mixed
*/
public function get_assertion($entityid) {
// V2 Only.
if ($this->backpackapiversion == OPEN_BADGES_V1) {
throw new coding_exception('Not supported in this backpack API');
}
return $this->curl_request('assertion', null, $entityid);
}
/**
* Get a badge class.
*
* @param string $entityid The id of the badge class.
* @return mixed
*/
public function get_badgeclass($entityid) {
// V2 Only.
if ($this->backpackapiversion == OPEN_BADGES_V1) {
throw new coding_exception('Not supported in this backpack API');
}
return $this->curl_request('badgeclass', null, $entityid);
}
/**
* Create a badgeclass assertion.
*
* @param string $entityid The id of the badge class.
* @param string $data The structure of the badge class assertion.
* @return mixed
*/
public function put_badgeclass_assertion($entityid, $data) {
// V2 Only.
if ($this->backpackapiversion == OPEN_BADGES_V1) {
throw new coding_exception('Not supported in this backpack API');
}
return $this->curl_request('assertions', null, $entityid, $data);
}
/**
* Select collections from a backpack.
*
* @param string $backpackid The id of the backpack
* @param stdClass[] $collections List of collections with collectionid or entityid.
* @return boolean
*/
public function set_backpack_collections($backpackid, $collections) {
global $DB, $USER;
// Delete any previously selected collections.
$sqlparams = array('backpack' => $backpackid);
$select = 'backpackid = :backpack ';
$DB->delete_records_select('badge_external', $select, $sqlparams);
$badgescache = cache::make('core', 'externalbadges');
// Insert selected collections if they are not in database yet.
foreach ($collections as $collection) {
$obj = new stdClass();
$obj->backpackid = $backpackid;
if ($this->backpackapiversion == OPEN_BADGES_V1) {
$obj->collectionid = (int) $collection;
} else {
$obj->entityid = $collection;
$obj->collectionid = -1;
}
if (!$DB->record_exists('badge_external', (array) $obj)) {
$DB->insert_record('badge_external', $obj);
}
}
$badgescache->delete($USER->id);
return true;
}
/**
* Create a badgeclass
*
* @param string $entityid The id of the entity.
* @param string $data The structure of the badge class.
* @return mixed
*/
public function put_badgeclass($entityid, $data) {
// V2 Only.
if ($this->backpackapiversion == OPEN_BADGES_V1) {
throw new coding_exception('Not supported in this backpack API');
}
return $this->curl_request('badgeclasses', null, $entityid, $data);
}
/**
* Create an issuer
*
* @param string $data The structure of the issuer.
* @return mixed
*/
public function put_issuer($data) {
// V2 Only.
if ($this->backpackapiversion == OPEN_BADGES_V1) {
throw new coding_exception('Not supported in this backpack API');
}
return $this->curl_request('issuers', null, null, $data);
}
/**
* Get an issuer by id
*
* @param string $entityid the id of the issuer.
* @return mixed
*/
public function get_issuer($entityid) {
// V2 Only.
if ($this->backpackapiversion == OPEN_BADGES_V1) {
throw new coding_exception('Not supported in this backpack API');
}
$result = $this->curl_request('issuer', null, $entityid);
return $result;
}
/**
* Authenticate using the stored email and password and save the valid access tokens.
*
* @return integer The id of the authenticated user.
*/
public function authenticate() {
global $SESSION;
$backpackidkey = $this->get_token_key(BADGE_BACKPACK_ID_TOKEN);
$backpackid = isset($SESSION->$backpackidkey) ? $SESSION->$backpackidkey : 0;
// If the backpack is changed we need to expire sessions.
if ($backpackid == $this->backpackid) {
if ($this->backpackapiversion == OPEN_BADGES_V2) {
$useridkey = $this->get_token_key(BADGE_USER_ID_TOKEN);
$authuserid = isset($SESSION->$useridkey) ? $SESSION->$useridkey : 0;
if ($authuserid == $this->get_auth_user_id()) {
$expireskey = $this->get_token_key(BADGE_EXPIRES_TOKEN);
if (isset($SESSION->$expireskey)) {
$expires = $SESSION->$expireskey;
if ($expires > time()) {
// We have a current access token for this user
// that has not expired.
return -1;
}
}
}
} else {
$useridkey = $this->get_token_key(BADGE_USER_ID_TOKEN);
$authuserid = isset($SESSION->$useridkey) ? $SESSION->$useridkey : 0;
if (!empty($authuserid)) {
return $authuserid;
}
}
}
return $this->curl_request('user', $this->email);
}
/**
* Get all collections in this backpack.
*
* @return stdClass[] The collections.
*/
public function get_collections() {
global $PAGE;
if ($this->authenticate()) {
if ($this->backpackapiversion == OPEN_BADGES_V1) {
$result = $this->curl_request('groups');
if (isset($result->groups)) {
$result = $result->groups;
}
} else {
$result = $this->curl_request('collections');
}
if ($result) {
return $result;
}
}
return [];
}
/**
* Get one collection by id.
*
* @param integer $collectionid
* @return stdClass The collection.
*/
public function get_collection_record($collectionid) {
global $DB;
if ($this->backpackapiversion == OPEN_BADGES_V1) {
return $DB->get_fieldset_select('badge_external', 'collectionid', 'backpackid = :bid', array('bid' => $collectionid));
} else {
return $DB->get_fieldset_select('badge_external', 'entityid', 'backpackid = :bid', array('bid' => $collectionid));
}
}
/**
* Disconnect the backpack from this user.
*
* @param integer $userid The user in Moodle
* @param integer $backpackid The backpack to disconnect
* @return boolean
*/
public function disconnect_backpack($userid, $backpackid) {
global $DB, $USER;
if (\core\session\manager::is_loggedinas() || $userid != $USER->id) {
// Can't change someone elses backpack settings.
return false;
}
$badgescache = cache::make('core', 'externalbadges');
$DB->delete_records('badge_external', array('backpackid' => $backpackid));
$DB->delete_records('badge_backpack', array('userid' => $userid));
$badgescache->delete($userid);
return true;
}
/**
* Handle the response from getting a collection to map to an id.
*
* @param stdClass $data The response data.
* @return string The collection id.
*/
public function get_collection_id_from_response($data) {
if ($this->backpackapiversion == OPEN_BADGES_V1) {
return $data->groupId;
} else {
return $data->entityId;
}
}
/**
* Get the list of badges in a collection.
*
* @param stdClass $collection The collection to deal with.
* @param boolean $expanded Fetch all the sub entities.
* @return stdClass[]
*/
public function get_badges($collection, $expanded = false) {
if ($this->authenticate()) {
if ($this->backpackapiversion == OPEN_BADGES_V1) {
if (empty($collection->collectionid)) {
return [];
}
$result = $this->curl_request('badges', $collection->collectionid);
return $result->badges;
} else {
if (empty($collection->entityid)) {
return [];
}
// Now we can make requests.
$badges = $this->curl_request('badges', $collection->entityid);
if (count($badges) == 0) {
return [];
}
$badges = $badges[0];
if ($expanded) {
$publicassertions = [];
foreach ($badges->assertions as $assertion) {
$remoteassertion = $this->get_assertion($assertion);
$remotebadge = $this->get_badgeclass($remoteassertion->badgeclass);
if (!$remotebadge) {
continue;
}
$remoteissuer = $this->get_issuer($remotebadge->issuer);
$badgeclone = clone $remotebadge;
$badgeclone->issuer = $remoteissuer;
$remoteassertion->badge = $badgeclone;
$remotebadge->assertion = $remoteassertion;
$publicassertions[] = $remotebadge;
}
$badges = $publicassertions;
}
return $badges;
}
}
}
}
+369
View File
@@ -0,0 +1,369 @@
<?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/>.
/**
* Represent the url for each method and the encoding of the parameters and response.
*
* @package core_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 <[email protected]>
*/
namespace core_badges;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/filelib.php');
use context_system;
use core_badges\external\assertion_exporter;
use core_badges\external\collection_exporter;
use core_badges\external\issuer_exporter;
use core_badges\external\badgeclass_exporter;
use curl;
/**
* Represent a single method for the remote api.
*/
class backpack_api_mapping {
/** @var string The action of this method. */
public $action;
/** @var string The base url of this backpack. */
private $url;
/** @var array List of parameters for this method. */
public $params;
/** @var string Name of a class to export parameters for this method. */
public $requestexporter;
/** @var string Name of a class to export response for this method. */
public $responseexporter;
/** @var boolean This method returns an array of responses. */
public $multiple;
/** @var string get or post methods. */
public $method;
/** @var boolean json decode the response. */
public $json;
/** @var boolean Authentication is required for this request. */
public $authrequired;
/** @var boolean Differentiate the function that can be called on a user backpack or a site backpack. */
private $isuserbackpack;
/**
* Create a mapping.
*
* @param string $action The action of this method.
* @param string $url The base url of this backpack.
* @param mixed $postparams List of parameters for this method.
* @param string $requestexporter Name of a class to export parameters for this method.
* @param string $responseexporter Name of a class to export response for this method.
* @param boolean $multiple This method returns an array of responses.
* @param string $method get or post methods.
* @param boolean $json json decode the response.
* @param boolean $authrequired Authentication is required for this request.
* @param boolean $isuserbackpack user backpack or a site backpack.
* @param integer $backpackapiversion OpenBadges version 1 or 2.
*/
public function __construct($action, $url, $postparams, $requestexporter, $responseexporter,
$multiple, $method, $json, $authrequired, $isuserbackpack, $backpackapiversion) {
$this->action = $action;
$this->url = $url;
$this->postparams = $postparams;
$this->requestexporter = $requestexporter;
$this->responseexporter = $responseexporter;
$this->multiple = $multiple;
$this->method = $method;
$this->json = $json;
$this->authrequired = $authrequired;
$this->isuserbackpack = $isuserbackpack;
$this->backpackapiversion = $backpackapiversion;
}
/**
* Get the unique key for the token.
*
* @param string $type The type of token.
* @return string
*/
private function get_token_key($type) {
$prefix = 'badges_';
if ($this->isuserbackpack) {
$prefix .= 'user_backpack_';
} else {
$prefix .= 'site_backpack_';
}
$prefix .= $type . '_token';
return $prefix;
}
/**
* Does the action match this mapping?
*
* @param string $action The action.
* @return boolean
*/
public function is_match($action) {
return $this->action == $action;
}
/**
* Parse the method url and insert parameters.
*
* @param string $apiurl The raw apiurl.
* @param string $param1 The first parameter.
* @param string $param2 The second parameter.
* @return string
*/
private function get_url($apiurl, $param1, $param2) {
$urlscheme = parse_url($apiurl, PHP_URL_SCHEME);
$urlhost = parse_url($apiurl, PHP_URL_HOST);
$url = $this->url;
$url = str_replace('[SCHEME]', $urlscheme, $url);
$url = str_replace('[HOST]', $urlhost, $url);
$url = str_replace('[URL]', $apiurl, $url);
$url = str_replace('[PARAM1]', $param1, $url);
$url = str_replace('[PARAM2]', $param2, $url);
return $url;
}
/**
* Parse the post parameters and insert replacements.
*
* @param string $email The api username.
* @param string $password The api password.
* @param string $param The parameter.
* @return mixed
*/
private function get_post_params($email, $password, $param) {
global $PAGE;
if ($this->method == 'get') {
return '';
}
$request = $this->postparams;
if ($request === '[PARAM]') {
$value = $param;
foreach ($value as $key => $keyvalue) {
if (gettype($value[$key]) == 'array') {
$newkey = 'related_' . $key;
$value[$newkey] = $value[$key];
unset($value[$key]);
}
}
} else if (is_array($request)) {
foreach ($request as $key => $value) {
if ($value == '[EMAIL]') {
$value = $email;
$request[$key] = $value;
} else if ($value == '[PASSWORD]') {
$value = $password;
$request[$key] = $value;
}
}
}
$context = context_system::instance();
$exporter = $this->requestexporter;
$output = $PAGE->get_renderer('core', 'badges');
if (!empty($exporter)) {
$exporterinstance = new $exporter($value, ['context' => $context]);
$request = $exporterinstance->export($output);
}
if ($this->json) {
return json_encode($request);
}
return $request;
}
/**
* Read the response from a V1 user request and save the userID.
*
* @param string $response The request response.
* @param integer $backpackid The backpack id.
* @return boolean
*/
private function convert_email_response($response, $backpackid) {
global $SESSION;
if (isset($response->status) && $response->status == 'okay') {
// Remember the tokens.
$useridkey = $this->get_token_key(BADGE_USER_ID_TOKEN);
$backpackidkey = $this->get_token_key(BADGE_BACKPACK_ID_TOKEN);
$SESSION->$useridkey = $response->userId;
$SESSION->$backpackidkey = $backpackid;
return $response->userId;
}
return false;
}
/**
* Get the user id from a previous user request.
*
* @return integer
*/
private function get_auth_user_id() {
global $USER;
if ($this->isuserbackpack) {
return $USER->id;
} else {
// The access tokens for the system backpack are shared.
return -1;
}
}
/**
* Parse the response from an openbadges 2 login.
*
* @param string $response The request response data.
* @param integer $backpackid The id of the backpack.
* @return mixed
*/
private function oauth_token_response($response, $backpackid) {
global $SESSION;
if (isset($response->access_token) && isset($response->refresh_token)) {
// Remember the tokens.
$accesskey = $this->get_token_key(BADGE_ACCESS_TOKEN);
$refreshkey = $this->get_token_key(BADGE_REFRESH_TOKEN);
$expireskey = $this->get_token_key(BADGE_EXPIRES_TOKEN);
$useridkey = $this->get_token_key(BADGE_USER_ID_TOKEN);
$backpackidkey = $this->get_token_key(BADGE_BACKPACK_ID_TOKEN);
if (isset($response->expires_in)) {
$timeout = $response->expires_in;
} else {
$timeout = 15 * 60; // 15 minute timeout if none set.
}
$expires = $timeout + time();
$SESSION->$expireskey = $expires;
$SESSION->$useridkey = $this->get_auth_user_id();
$SESSION->$accesskey = $response->access_token;
$SESSION->$refreshkey = $response->refresh_token;
$SESSION->$backpackidkey = $backpackid;
return -1;
}
return $response;
}
/**
* Standard options used for all curl requests.
*
* @return array
*/
private function get_curl_options() {
return array(
'FRESH_CONNECT' => true,
'RETURNTRANSFER' => true,
'FORBID_REUSE' => true,
'HEADER' => 0,
'CONNECTTIMEOUT' => 3,
'CONNECTTIMEOUT' => 3,
// Follow redirects with the same type of request when sent 301, or 302 redirects.
'CURLOPT_POSTREDIR' => 3,
);
}
/**
* Make an api request and parse the response.
*
* @param string $apiurl Raw request url.
* @param string $urlparam1 Parameter for the request.
* @param string $urlparam2 Parameter for the request.
* @param string $email User email for authentication.
* @param string $password for authentication.
* @param mixed $postparam Raw data for the post body.
* @param string $backpackid the id of the backpack to use.
* @return mixed
*/
public function request($apiurl, $urlparam1, $urlparam2, $email, $password, $postparam, $backpackid) {
global $SESSION, $PAGE;
$curl = new curl();
$url = $this->get_url($apiurl, $urlparam1, $urlparam2);
if ($this->authrequired) {
$accesskey = $this->get_token_key(BADGE_ACCESS_TOKEN);
if (isset($SESSION->$accesskey)) {
$token = $SESSION->$accesskey;
$curl->setHeader('Authorization: Bearer ' . $token);
}
}
if ($this->json) {
$curl->setHeader(array('Content-type: application/json'));
}
$curl->setHeader(array('Accept: application/json', 'Expect:'));
$options = $this->get_curl_options();
$post = $this->get_post_params($email, $password, $postparam);
if ($this->method == 'get') {
$response = $curl->get($url, $post, $options);
} else if ($this->method == 'post') {
$response = $curl->post($url, $post, $options);
}
$response = json_decode($response);
if (isset($response->result)) {
$response = $response->result;
}
$context = context_system::instance();
$exporter = $this->responseexporter;
if (class_exists($exporter)) {
$output = $PAGE->get_renderer('core', 'badges');
if (!$this->multiple) {
if (count($response)) {
$response = $response[0];
}
if (empty($response)) {
return null;
}
$apidata = $exporter::map_external_data($response, $this->backpackapiversion);
$exporterinstance = new $exporter($apidata, ['context' => $context]);
$data = $exporterinstance->export($output);
return $data;
} else {
$multiple = [];
if (empty($response)) {
return $multiple;
}
foreach ($response as $data) {
$apidata = $exporter::map_external_data($data, $this->backpackapiversion);
$exporterinstance = new $exporter($apidata, ['context' => $context]);
$multiple[] = $exporterinstance->export($output);
}
return $multiple;
}
} else if (method_exists($this, $exporter)) {
return $this->$exporter($response, $backpackid);
}
return $response;
}
}
+929
View File
@@ -0,0 +1,929 @@
<?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 <[email protected]>
*/
namespace core_badges;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/lib/badgeslib.php');
use context_system;
use context_course;
use context_user;
use moodle_exception;
use moodle_url;
use core_text;
use award_criteria;
use core_php_time_limit;
use html_writer;
use stdClass;
/**
* Class that represents badge.
*
*/
class badge {
/** @var int Badge id */
public $id;
/** @var string Badge name */
public $name;
/** @var string Badge description */
public $description;
/** @var integer Timestamp this badge was created */
public $timecreated;
/** @var integer Timestamp this badge was modified */
public $timemodified;
/** @var int The user who created this badge */
public $usercreated;
/** @var int The user who modified this badge */
public $usermodified;
/** @var string The name of the issuer of this badge */
public $issuername;
/** @var string The url of the issuer of this badge */
public $issuerurl;
/** @var string The email of the issuer of this badge */
public $issuercontact;
/** @var integer Timestamp this badge will expire */
public $expiredate;
/** @var integer Duration this badge is valid for */
public $expireperiod;
/** @var integer Site or course badge */
public $type;
/** @var integer The course this badge belongs to */
public $courseid;
/** @var string The message this badge includes. */
public $message;
/** @var string The subject of the message for this badge */
public $messagesubject;
/** @var int Is this badge image baked. */
public $attachment;
/** @var int Send a message when this badge is awarded. */
public $notification;
/** @var int Lifecycle status for this badge. */
public $status = 0;
/** @var int Timestamp to next run cron for this badge. */
public $nextcron;
/** @var int What backpack api version to use for this badge. */
public $version;
/** @var string What language is this badge written in. */
public $language;
/** @var string The author of the image for this badge. */
public $imageauthorname;
/** @var string The email of the author of the image for this badge. */
public $imageauthoremail;
/** @var string The url of the author of the image for this badge. */
public $imageauthorurl;
/** @var string The caption of the image for this badge. */
public $imagecaption;
/** @var array Badge criteria */
public $criteria = array();
/**
* Constructs with badge details.
*
* @param int $badgeid badge ID.
*/
public function __construct($badgeid) {
global $DB;
$this->id = $badgeid;
$data = $DB->get_record('badge', array('id' => $badgeid));
if (empty($data)) {
print_error('error:nosuchbadge', 'badges', $badgeid);
}
foreach ((array)$data as $field => $value) {
if (property_exists($this, $field)) {
$this->{$field} = $value;
}
}
$this->criteria = self::get_criteria();
}
/**
* Use to get context instance of a badge.
*
* @return context instance.
*/
public function get_context() {
if ($this->type == BADGE_TYPE_SITE) {
return context_system::instance();
} else if ($this->type == BADGE_TYPE_COURSE) {
return context_course::instance($this->courseid);
} else {
debugging('Something is wrong...');
}
}
/**
* Return array of aggregation methods
*
* @return array
*/
public static function get_aggregation_methods() {
return array(
BADGE_CRITERIA_AGGREGATION_ALL => get_string('all', 'badges'),
BADGE_CRITERIA_AGGREGATION_ANY => get_string('any', 'badges'),
);
}
/**
* Return array of accepted criteria types for this badge
*
* @return array
*/
public function get_accepted_criteria() {
global $CFG;
$criteriatypes = array();
if ($this->type == BADGE_TYPE_COURSE) {
$criteriatypes = array(
BADGE_CRITERIA_TYPE_OVERALL,
BADGE_CRITERIA_TYPE_MANUAL,
BADGE_CRITERIA_TYPE_COURSE,
BADGE_CRITERIA_TYPE_BADGE,
BADGE_CRITERIA_TYPE_ACTIVITY,
BADGE_CRITERIA_TYPE_COMPETENCY
);
} else if ($this->type == BADGE_TYPE_SITE) {
$criteriatypes = array(
BADGE_CRITERIA_TYPE_OVERALL,
BADGE_CRITERIA_TYPE_MANUAL,
BADGE_CRITERIA_TYPE_COURSESET,
BADGE_CRITERIA_TYPE_BADGE,
BADGE_CRITERIA_TYPE_PROFILE,
BADGE_CRITERIA_TYPE_COHORT,
BADGE_CRITERIA_TYPE_COMPETENCY
);
}
$alltypes = badges_list_criteria();
foreach ($criteriatypes as $index => $type) {
if (!isset($alltypes[$type])) {
unset($criteriatypes[$index]);
}
}
return $criteriatypes;
}
/**
* Save/update badge information in 'badge' table only.
* Cannot be used for updating awards and criteria settings.
*
* @return boolean Returns true on success.
*/
public function save() {
global $DB;
$fordb = new stdClass();
foreach (get_object_vars($this) as $k => $v) {
$fordb->{$k} = $v;
}
unset($fordb->criteria);
$fordb->timemodified = time();
if ($DB->update_record_raw('badge', $fordb)) {
// Trigger event, badge updated.
$eventparams = array('objectid' => $this->id, 'context' => $this->get_context());
$event = \core\event\badge_updated::create($eventparams);
$event->trigger();
return true;
} else {
throw new moodle_exception('error:save', 'badges');
return false;
}
}
/**
* Creates and saves a clone of badge with all its properties.
* Clone is not active by default and has 'Copy of' attached to its name.
*
* @return int ID of new badge.
*/
public function make_clone() {
global $DB, $USER, $PAGE;
$fordb = new stdClass();
foreach (get_object_vars($this) as $k => $v) {
$fordb->{$k} = $v;
}
$fordb->name = get_string('copyof', 'badges', $this->name);
$fordb->status = BADGE_STATUS_INACTIVE;
$fordb->usercreated = $USER->id;
$fordb->usermodified = $USER->id;
$fordb->timecreated = time();
$fordb->timemodified = time();
unset($fordb->id);
if ($fordb->notification > 1) {
$fordb->nextcron = badges_calculate_message_schedule($fordb->notification);
}
$criteria = $fordb->criteria;
unset($fordb->criteria);
if ($new = $DB->insert_record('badge', $fordb, true)) {
$newbadge = new badge($new);
// Copy badge image.
$fs = get_file_storage();
if ($file = $fs->get_file($this->get_context()->id, 'badges', 'badgeimage', $this->id, '/', 'f1.png')) {
if ($imagefile = $file->copy_content_to_temp()) {
badges_process_badge_image($newbadge, $imagefile);
}
}
// Copy badge criteria.
foreach ($this->criteria as $crit) {
$crit->make_clone($new);
}
// Trigger event, badge duplicated.
$eventparams = array('objectid' => $new, 'context' => $PAGE->context);
$event = \core\event\badge_duplicated::create($eventparams);
$event->trigger();
return $new;
} else {
throw new moodle_exception('error:clone', 'badges');
return false;
}
}
/**
* Checks if badges is active.
* Used in badge award.
*
* @return boolean A status indicating badge is active
*/
public function is_active() {
if (($this->status == BADGE_STATUS_ACTIVE) ||
($this->status == BADGE_STATUS_ACTIVE_LOCKED)) {
return true;
}
return false;
}
/**
* Use to get the name of badge status.
*
* @return string
*/
public function get_status_name() {
return get_string('badgestatus_' . $this->status, 'badges');
}
/**
* Use to set badge status.
* Only active badges can be earned/awarded/issued.
*
* @param int $status Status from BADGE_STATUS constants
*/
public function set_status($status = 0) {
$this->status = $status;
$this->save();
if ($status == BADGE_STATUS_ACTIVE) {
// Trigger event, badge enabled.
$eventparams = array('objectid' => $this->id, 'context' => $this->get_context());
$event = \core\event\badge_enabled::create($eventparams);
$event->trigger();
} else if ($status == BADGE_STATUS_INACTIVE) {
// Trigger event, badge disabled.
$eventparams = array('objectid' => $this->id, 'context' => $this->get_context());
$event = \core\event\badge_disabled::create($eventparams);
$event->trigger();
}
}
/**
* Checks if badges is locked.
* Used in badge award and editing.
*
* @return boolean A status indicating badge is locked
*/
public function is_locked() {
if (($this->status == BADGE_STATUS_ACTIVE_LOCKED) ||
($this->status == BADGE_STATUS_INACTIVE_LOCKED)) {
return true;
}
return false;
}
/**
* Checks if badge has been awarded to users.
* Used in badge editing.
*
* @return boolean A status indicating badge has been awarded at least once
*/
public function has_awards() {
global $DB;
$awarded = $DB->record_exists_sql('SELECT b.uniquehash
FROM {badge_issued} b INNER JOIN {user} u ON b.userid = u.id
WHERE b.badgeid = :badgeid AND u.deleted = 0', array('badgeid' => $this->id));
return $awarded;
}
/**
* Gets list of users who have earned an instance of this badge.
*
* @return array An array of objects with information about badge awards.
*/
public function get_awards() {
global $DB;
$awards = $DB->get_records_sql(
'SELECT b.userid, b.dateissued, b.uniquehash, u.firstname, u.lastname
FROM {badge_issued} b INNER JOIN {user} u
ON b.userid = u.id
WHERE b.badgeid = :badgeid AND u.deleted = 0', array('badgeid' => $this->id));
return $awards;
}
/**
* Indicates whether badge has already been issued to a user.
*
*/
public function is_issued($userid) {
global $DB;
return $DB->record_exists('badge_issued', array('badgeid' => $this->id, 'userid' => $userid));
}
/**
* Issue a badge to user.
*
* @param int $userid User who earned the badge
* @param boolean $nobake Not baking actual badges (for testing purposes)
*/
public function issue($userid, $nobake = false) {
global $DB, $CFG;
$now = time();
$issued = new stdClass();
$issued->badgeid = $this->id;
$issued->userid = $userid;
$issued->uniquehash = sha1(rand() . $userid . $this->id . $now);
$issued->dateissued = $now;
if ($this->can_expire()) {
$issued->dateexpire = $this->calculate_expiry($now);
} else {
$issued->dateexpire = null;
}
// Take into account user badges privacy settings.
// If none set, badges default visibility is set to public.
$issued->visible = get_user_preferences('badgeprivacysetting', 1, $userid);
$result = $DB->insert_record('badge_issued', $issued, true);
if ($result) {
// Trigger badge awarded event.
$eventdata = array (
'context' => $this->get_context(),
'objectid' => $this->id,
'relateduserid' => $userid,
'other' => array('dateexpire' => $issued->dateexpire, 'badgeissuedid' => $result)
);
\core\event\badge_awarded::create($eventdata)->trigger();
// Lock the badge, so that its criteria could not be changed any more.
if ($this->status == BADGE_STATUS_ACTIVE) {
$this->set_status(BADGE_STATUS_ACTIVE_LOCKED);
}
// Update details in criteria_met table.
$compl = $this->get_criteria_completions($userid);
foreach ($compl as $c) {
$obj = new stdClass();
$obj->id = $c->id;
$obj->issuedid = $result;
$DB->update_record('badge_criteria_met', $obj, true);
}
if (!$nobake) {
// Bake a badge image.
$pathhash = badges_bake($issued->uniquehash, $this->id, $userid, true);
// Notify recipients and badge creators.
badges_notify_badge_award($this, $userid, $issued->uniquehash, $pathhash);
}
}
}
/**
* Reviews all badge criteria and checks if badge can be instantly awarded.
*
* @return int Number of awards
*/
public function review_all_criteria() {
global $DB, $CFG;
$awards = 0;
// Raise timelimit as this could take a while for big web sites.
core_php_time_limit::raise();
raise_memory_limit(MEMORY_HUGE);
foreach ($this->criteria as $crit) {
// Overall criterion is decided when other criteria are reviewed.
if ($crit->criteriatype == BADGE_CRITERIA_TYPE_OVERALL) {
continue;
}
list($extrajoin, $extrawhere, $extraparams) = $crit->get_completed_criteria_sql();
// For site level badges, get all active site users who can earn this badge and haven't got it yet.
if ($this->type == BADGE_TYPE_SITE) {
$sql = "SELECT DISTINCT u.id, bi.badgeid
FROM {user} u
{$extrajoin}
LEFT JOIN {badge_issued} bi
ON u.id = bi.userid AND bi.badgeid = :badgeid
WHERE bi.badgeid IS NULL AND u.id != :guestid AND u.deleted = 0 " . $extrawhere;
$params = array_merge(array('badgeid' => $this->id, 'guestid' => $CFG->siteguest), $extraparams);
$toearn = $DB->get_fieldset_sql($sql, $params);
} else {
// For course level badges, get all users who already earned the badge in this course.
// Then find the ones who are enrolled in the course and don't have a badge yet.
$earned = $DB->get_fieldset_select(
'badge_issued',
'userid AS id',
'badgeid = :badgeid',
array('badgeid' => $this->id)
);
$wheresql = '';
$earnedparams = array();
if (!empty($earned)) {
list($earnedsql, $earnedparams) = $DB->get_in_or_equal($earned, SQL_PARAMS_NAMED, 'u', false);
$wheresql = ' WHERE u.id ' . $earnedsql;
}
list($enrolledsql, $enrolledparams) = get_enrolled_sql($this->get_context(), 'moodle/badges:earnbadge', 0, true);
$sql = "SELECT DISTINCT u.id
FROM {user} u
{$extrajoin}
JOIN ({$enrolledsql}) je ON je.id = u.id " . $wheresql . $extrawhere;
$params = array_merge($enrolledparams, $earnedparams, $extraparams);
$toearn = $DB->get_fieldset_sql($sql, $params);
}
foreach ($toearn as $uid) {
$reviewoverall = false;
if ($crit->review($uid, true)) {
$crit->mark_complete($uid);
if ($this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->method == BADGE_CRITERIA_AGGREGATION_ANY) {
$this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($uid);
$this->issue($uid);
$awards++;
} else {
$reviewoverall = true;
}
} else {
// Will be reviewed some other time.
$reviewoverall = false;
}
// Review overall if it is required.
if ($reviewoverall && $this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($uid)) {
$this->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($uid);
$this->issue($uid);
$awards++;
}
}
}
return $awards;
}
/**
* Gets an array of completed criteria from 'badge_criteria_met' table.
*
* @param int $userid Completions for a user
* @return array Records of criteria completions
*/
public function get_criteria_completions($userid) {
global $DB;
$completions = array();
$sql = "SELECT bcm.id, bcm.critid
FROM {badge_criteria_met} bcm
INNER JOIN {badge_criteria} bc ON bcm.critid = bc.id
WHERE bc.badgeid = :badgeid AND bcm.userid = :userid ";
$completions = $DB->get_records_sql($sql, array('badgeid' => $this->id, 'userid' => $userid));
return $completions;
}
/**
* Checks if badges has award criteria set up.
*
* @return boolean A status indicating badge has at least one criterion
*/
public function has_criteria() {
if (count($this->criteria) > 0) {
return true;
}
return false;
}
/**
* Returns badge award criteria
*
* @return array An array of badge criteria
*/
public function get_criteria() {
global $DB;
$criteria = array();
if ($records = (array)$DB->get_records('badge_criteria', array('badgeid' => $this->id))) {
foreach ($records as $record) {
$criteria[$record->criteriatype] = award_criteria::build((array)$record);
}
}
return $criteria;
}
/**
* Get aggregation method for badge criteria
*
* @param int $criteriatype If none supplied, get overall aggregation method (optional)
* @return int One of BADGE_CRITERIA_AGGREGATION_ALL or BADGE_CRITERIA_AGGREGATION_ANY
*/
public function get_aggregation_method($criteriatype = 0) {
global $DB;
$params = array('badgeid' => $this->id, 'criteriatype' => $criteriatype);
$aggregation = $DB->get_field('badge_criteria', 'method', $params, IGNORE_MULTIPLE);
if (!$aggregation) {
return BADGE_CRITERIA_AGGREGATION_ALL;
}
return $aggregation;
}
/**
* Checks if badge has expiry period or date set up.
*
* @return boolean A status indicating badge can expire
*/
public function can_expire() {
if ($this->expireperiod || $this->expiredate) {
return true;
}
return false;
}
/**
* Calculates badge expiry date based on either expirydate or expiryperiod.
*
* @param int $timestamp Time of badge issue
* @return int A timestamp
*/
public function calculate_expiry($timestamp) {
$expiry = null;
if (isset($this->expiredate)) {
$expiry = $this->expiredate;
} else if (isset($this->expireperiod)) {
$expiry = $timestamp + $this->expireperiod;
}
return $expiry;
}
/**
* Checks if badge has manual award criteria set.
*
* @return boolean A status indicating badge can be awarded manually
*/
public function has_manual_award_criteria() {
foreach ($this->criteria as $criterion) {
if ($criterion->criteriatype == BADGE_CRITERIA_TYPE_MANUAL) {
return true;
}
}
return false;
}
/**
* Fully deletes the badge or marks it as archived.
*
* @param $archive boolean Achive a badge without actual deleting of any data.
*/
public function delete($archive = true) {
global $DB;
if ($archive) {
$this->status = BADGE_STATUS_ARCHIVED;
$this->save();
// Trigger event, badge archived.
$eventparams = array('objectid' => $this->id, 'context' => $this->get_context());
$event = \core\event\badge_archived::create($eventparams);
$event->trigger();
return;
}
$fs = get_file_storage();
// Remove all issued badge image files and badge awards.
// Cannot bulk remove area files here because they are issued in user context.
$awards = $this->get_awards();
foreach ($awards as $award) {
$usercontext = context_user::instance($award->userid);
$fs->delete_area_files($usercontext->id, 'badges', 'userbadge', $this->id);
}
$DB->delete_records('badge_issued', array('badgeid' => $this->id));
// Remove all badge criteria.
$criteria = $this->get_criteria();
foreach ($criteria as $criterion) {
$criterion->delete();
}
// Delete badge images.
$badgecontext = $this->get_context();
$fs->delete_area_files($badgecontext->id, 'badges', 'badgeimage', $this->id);
// Delete endorsements, competencies and related badges.
$DB->delete_records('badge_endorsement', array('badgeid' => $this->id));
$relatedsql = 'badgeid = :badgeid OR relatedbadgeid = :relatedbadgeid';
$relatedparams = array(
'badgeid' => $this->id,
'relatedbadgeid' => $this->id
);
$DB->delete_records_select('badge_related', $relatedsql, $relatedparams);
$DB->delete_records('badge_alignment', array('badgeid' => $this->id));
// Finally, remove badge itself.
$DB->delete_records('badge', array('id' => $this->id));
// Trigger event, badge deleted.
$eventparams = array('objectid' => $this->id,
'context' => $this->get_context(),
'other' => array('badgetype' => $this->type, 'courseid' => $this->courseid)
);
$event = \core\event\badge_deleted::create($eventparams);
$event->trigger();
}
/**
* Add multiple related badges.
*
* @param array $relatedids Id of badges.
*/
public function add_related_badges($relatedids) {
global $DB;
$relatedbadges = array();
foreach ($relatedids as $relatedid) {
$relatedbadge = new stdClass();
$relatedbadge->badgeid = $this->id;
$relatedbadge->relatedbadgeid = $relatedid;
$relatedbadges[] = $relatedbadge;
}
$DB->insert_records('badge_related', $relatedbadges);
}
/**
* Delete an related badge.
*
* @param int $relatedid Id related badge.
* @return boolean A status for delete an related badge.
*/
public function delete_related_badge($relatedid) {
global $DB;
$sql = "(badgeid = :badgeid AND relatedbadgeid = :relatedid) OR " .
"(badgeid = :relatedid2 AND relatedbadgeid = :badgeid2)";
$params = ['badgeid' => $this->id, 'badgeid2' => $this->id, 'relatedid' => $relatedid, 'relatedid2' => $relatedid];
return $DB->delete_records_select('badge_related', $sql, $params);
}
/**
* Checks if badge has related badges.
*
* @return boolean A status related badge.
*/
public function has_related() {
global $DB;
$sql = "SELECT DISTINCT b.id
FROM {badge_related} br
JOIN {badge} b ON (br.relatedbadgeid = b.id OR br.badgeid = b.id)
WHERE (br.badgeid = :badgeid OR br.relatedbadgeid = :badgeid2) AND b.id != :badgeid3";
return $DB->record_exists_sql($sql, ['badgeid' => $this->id, 'badgeid2' => $this->id, 'badgeid3' => $this->id]);
}
/**
* Get related badges of badge.
*
* @param boolean $activeonly Do not get the inactive badges when is true.
* @return array Related badges information.
*/
public function get_related_badges($activeonly = false) {
global $DB;
$params = array('badgeid' => $this->id, 'badgeid2' => $this->id, 'badgeid3' => $this->id);
$query = "SELECT DISTINCT b.id, b.name, b.version, b.language, b.type
FROM {badge_related} br
JOIN {badge} b ON (br.relatedbadgeid = b.id OR br.badgeid = b.id)
WHERE (br.badgeid = :badgeid OR br.relatedbadgeid = :badgeid2) AND b.id != :badgeid3";
if ($activeonly) {
$query .= " AND b.status <> :status";
$params['status'] = BADGE_STATUS_INACTIVE;
}
$relatedbadges = $DB->get_records_sql($query, $params);
return $relatedbadges;
}
/**
* Insert/update alignment information of badge.
*
* @param stdClass $alignment Data of a alignment.
* @param int $alignmentid ID alignment.
* @return bool|int A status/ID when insert or update data.
*/
public function save_alignment($alignment, $alignmentid = 0) {
global $DB;
$record = $DB->record_exists('badge_alignment', array('id' => $alignmentid));
if ($record) {
$alignment->id = $alignmentid;
return $DB->update_record('badge_alignment', $alignment);
} else {
return $DB->insert_record('badge_alignment', $alignment, true);
}
}
/**
* Delete a alignment of badge.
*
* @param int $alignmentid ID alignment.
* @return boolean A status for delete a alignment.
*/
public function delete_alignment($alignmentid) {
global $DB;
return $DB->delete_records('badge_alignment', array('badgeid' => $this->id, 'id' => $alignmentid));
}
/**
* Get alignments of badge.
*
* @return array List content alignments.
*/
public function get_alignments() {
global $DB;
return $DB->get_records('badge_alignment', array('badgeid' => $this->id));
}
/**
* Insert/update Endorsement information of badge.
*
* @param stdClass $endorsement Data of an endorsement.
* @return bool|int A status/ID when insert or update data.
*/
public function save_endorsement($endorsement) {
global $DB;
$record = $DB->get_record('badge_endorsement', array('badgeid' => $this->id));
if ($record) {
$endorsement->id = $record->id;
return $DB->update_record('badge_endorsement', $endorsement);
} else {
return $DB->insert_record('badge_endorsement', $endorsement, true);
}
}
/**
* Get endorsement of badge.
*
* @return array|stdClass Endorsement information.
*/
public function get_endorsement() {
global $DB;
return $DB->get_record('badge_endorsement', array('badgeid' => $this->id));
}
/**
* Markdown language support for criteria.
*
* @return string $output Markdown content to output.
*/
public function markdown_badge_criteria() {
$agg = $this->get_aggregation_methods();
if (empty($this->criteria)) {
return get_string('nocriteria', 'badges');
}
$overalldescr = '';
$overall = $this->criteria[BADGE_CRITERIA_TYPE_OVERALL];
if (!empty($overall->description)) {
$overalldescr = format_text($overall->description, $overall->descriptionformat,
array('context' => $this->get_context())) . '\n';
}
// Get the condition string.
if (count($this->criteria) == 2) {
$condition = get_string('criteria_descr', 'badges');
} else {
$condition = get_string('criteria_descr_' . BADGE_CRITERIA_TYPE_OVERALL, 'badges',
core_text::strtoupper($agg[$this->get_aggregation_method()]));
}
unset($this->criteria[BADGE_CRITERIA_TYPE_OVERALL]);
$items = array();
// If only one criterion left, make sure its description goe to the top.
if (count($this->criteria) == 1) {
$c = reset($this->criteria);
if (!empty($c->description)) {
$overalldescr = $c->description . '\n';
}
if (count($c->params) == 1) {
$items[] = ' * ' . get_string('criteria_descr_single_' . $c->criteriatype, 'badges') .
$c->get_details();
} else {
$items[] = '* ' . get_string('criteria_descr_' . $c->criteriatype, 'badges',
core_text::strtoupper($agg[$this->get_aggregation_method($c->criteriatype)])) .
$c->get_details();
}
} else {
foreach ($this->criteria as $type => $c) {
$criteriadescr = '';
if (!empty($c->description)) {
$criteriadescr = $c->description;
}
if (count($c->params) == 1) {
$items[] = ' * ' . get_string('criteria_descr_single_' . $type, 'badges') .
$c->get_details() . $criteriadescr;
} else {
$items[] = '* ' . get_string('criteria_descr_' . $type, 'badges',
core_text::strtoupper($agg[$this->get_aggregation_method($type)])) .
$c->get_details() . $criteriadescr;
}
}
}
return strip_tags($overalldescr . $condition . html_writer::alist($items, array(), 'ul'));
}
/**
* Define issuer information by format Open Badges specification version 2.
*
* @return array Issuer informations of the badge.
*/
public function get_badge_issuer() {
$issuer = array();
$issuerurl = new moodle_url('/badges/badge_json.php', array('id' => $this->id, 'action' => 0));
$issuer['name'] = $this->issuername;
$issuer['url'] = $this->issuerurl;
$issuer['email'] = $this->issuercontact;
$issuer['@context'] = OPEN_BADGES_V2_CONTEXT;
$issuer['id'] = $issuerurl->out(false);
$issuer['type'] = OPEN_BADGES_V2_TYPE_ISSUER;
return $issuer;
}
}
+9 -5
View File
@@ -47,32 +47,36 @@ class alignment_exporter extends exporter {
'id' => [
'type' => PARAM_INT,
'description' => 'Alignment id',
'optional' => true,
],
'badgeid' => [
'type' => PARAM_INT,
'description' => 'Badge id',
'optional' => true,
],
'targetname' => [
'targetName' => [
'type' => PARAM_TEXT,
'description' => 'Target name',
'optional' => true,
],
'targeturl' => [
'targetUrl' => [
'type' => PARAM_URL,
'description' => 'Target URL',
'optional' => true,
],
'targetdescription' => [
'targetDescription' => [
'type' => PARAM_TEXT,
'description' => 'Target description',
'null' => NULL_ALLOWED,
'optional' => true,
],
'targetframework' => [
'targetFramework' => [
'type' => PARAM_TEXT,
'description' => 'Target framework',
'null' => NULL_ALLOWED,
'optional' => true,
],
'targetcode' => [
'targetCode' => [
'type' => PARAM_TEXT,
'description' => 'Target code',
'null' => NULL_ALLOWED,
+160
View File
@@ -0,0 +1,160 @@
<?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/>.
/**
* Contains class for displaying a assertion.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_badges\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
use stdClass;
/**
* Class for displaying a badge competency.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class assertion_exporter extends exporter {
/**
* Map from a request response data to the internal structure.
*
* @param stdClass $data The remote data.
* @param string $apiversion The backpack version used to communicate remotely.
* @return stdClass
*/
public static function map_external_data($data, $apiversion) {
$mapped = new \stdClass();
if (isset($data->entityType)) {
$mapped->type = $data->entityType;
} else {
$mapped->type = $data->type;
}
if (isset($data->entityId)) {
$mapped->id = $data->entityId;
} else {
$mapped->id = $data->id;
}
if (isset($data->issuedOn)) {
$mapped->issuedOn = $data->issuedOn;
}
if (isset($data->recipient)) {
$mapped->recipient = $data->recipient;
}
if (isset($data->badgeclass)) {
$mapped->badgeclass = $data->badgeclass;
}
$propname = '@context';
$mapped->$propname = 'https://w3id.org/openbadges/v2';
return $mapped;
}
protected static function define_other_properties() {
return array(
'badge' => array(
'type' => badgeclass_exporter::read_properties_definition(),
'optional' => true
),
'recipient' => array(
'type' => recipient_exporter::read_properties_definition(),
'optional' => true
),
'verification' => array(
'type' => verification_exporter::read_properties_definition(),
'optional' => true
)
);
}
/**
* We map from related data passed as data to this exporter to clean exportable values.
*/
protected function get_other_values(renderer_base $output) {
global $DB;
$result = [];
if (array_key_exists('related_badge', $this->data)) {
$exporter = new badgeclass_exporter($this->data['related_badge'], $this->related);
$result['badge'] = $exporter->export($output);
}
if (array_key_exists('related_recipient', $this->data)) {
$exporter = new recipient_exporter($this->data['related_recipient'], $this->related);
$result['recipient'] = $exporter->export($output);
}
if (array_key_exists('related_verify', $this->data)) {
$exporter = new verification_exporter($this->data['related_verify'], $this->related);
$result['verification'] = $exporter->export($output);
}
return $result;
}
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'type' => [
'type' => PARAM_ALPHA,
'description' => 'Issuer',
],
'id' => [
'type' => PARAM_URL,
'description' => 'Unique identifier for this assertion',
],
'badgeclass' => [
'type' => PARAM_RAW,
'description' => 'Identifier of the badge for this assertion',
'optional' => true,
],
'issuedOn' => [
'type' => PARAM_RAW,
'description' => 'Date this badge was issued',
],
'expires' => [
'type' => PARAM_RAW,
'description' => 'Date this badge will expire',
'optional' => true,
],
'@context' => [
'type' => PARAM_URL,
'description' => 'Badge version',
],
];
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return array(
'context' => 'context'
);
}
}
+73
View File
@@ -0,0 +1,73 @@
<?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/>.
/**
* Contains competency class for displaying a badge backpack.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_badges\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
/**
* Class for displaying a badge competency.
*
* @package core_badges
* @copyright 2018 Dani Palou <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backpack_exporter extends exporter {
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'id' => [
'type' => PARAM_INT,
'description' => 'Backpack id',
],
'backpackapiurl' => [
'type' => PARAM_URL,
'description' => 'Backpack API URL',
],
'backpackweburl' => [
'type' => PARAM_URL,
'description' => 'Backpack Website URL',
],
'sitebackpack' => [
'type' => PARAM_BOOL,
'description' => 'Is this the current site backpack',
],
'apiversion' => [
'type' => PARAM_FLOAT,
'description' => 'API version supported',
],
'sortorder' => [
'type' => PARAM_INT,
'description' => 'Sort order'
]
];
}
}
+227
View File
@@ -0,0 +1,227 @@
<?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/>.
/**
* Contains class for displaying a badgeclass.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_badges\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
/**
* Class for displaying a badge competency.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class badgeclass_exporter extends exporter {
/**
* Constructor - saves the persistent object, and the related objects.
*
* @param mixed $data - Either an stdClass or an array of values.
* @param array $related - An optional list of pre-loaded objects related to this object.
*/
public function __construct($data, $related = array()) {
$pick = $this->pick_related();
foreach ($pick as $one) {
$isarray = false;
// Allow [] to mean an array of values.
if (substr($one, -2) === '[]') {
$one = substr($one, 0, -2);
$isarray = true;
}
$prefixed = 'related_' . $one;
if (array_key_exists($one, $data) && !array_key_exists($one, $related)) {
if ($isarray) {
$newrelated = [];
foreach ($data[$one] as $item) {
$newrelated[] = (object) $item;
}
$related[$one] = $newrelated;
} else {
$related[$one] = (object) $data[$one];
}
unset($data[$one]);
} else if (array_key_exists($prefixed, $data) && !array_key_exists($one, $related)) {
if ($isarray) {
$newrelated = [];
foreach ($data[$prefixed] as $item) {
$newrelated[] = (object) $item;
}
$related[$one] = $newrelated;
} else {
$related[$one] = (object) $data[$prefixed];
}
unset($data[$prefixed]);
} else if (!array_key_exists($one, $related)) {
$related[$one] = null;
}
}
parent::__construct($data, $related);
}
/**
* List properties passed in $data that should be moved to $related in the constructor.
*
* @return array A list of properties to move from $data to $related.
*/
public static function pick_related() {
return ['alignments[]', 'criteria'];
}
/**
* Map data from a request response to the internal structure.
*
* @param stdClass $data The remote data.
* @param string $apiversion The backpack version used to communicate remotely.
* @return stdClass
*/
public static function map_external_data($data, $apiversion) {
$mapped = new \stdClass();
if (isset($data->entityType)) {
$mapped->type = $data->entityType;
} else {
$mapped->type = $data->type;
}
if (isset($data->entityId)) {
$mapped->id = $data->entityId;
} else {
$mapped->id = $data->id;
}
$mapped->name = $data->name;
$mapped->image = $data->image;
$mapped->issuer = $data->issuer;
$mapped->description = $data->description;
if (isset($data->openBadgeId)) {
$mapped->hostedUrl = $data->openBadgeId;
}
return $mapped;
}
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'type' => [
'type' => PARAM_ALPHA,
'description' => 'BadgeClass',
],
'id' => [
'type' => PARAM_RAW,
'description' => 'Unique identifier for this badgeclass',
],
'issuer' => [
'type' => PARAM_RAW,
'description' => 'Unique identifier for this badgeclass',
'optional' => true,
],
'name' => [
'type' => PARAM_TEXT,
'description' => 'Name of the badgeclass',
],
'image' => [
'type' => PARAM_URL,
'description' => 'URL to the image.',
],
'description' => [
'type' => PARAM_TEXT,
'description' => 'Description of the badge class.',
],
'hostedUrl' => [
'type' => PARAM_RAW,
'description' => 'Identifier of the open badge for this assertion',
'optional' => true,
],
];
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return array(
'context' => 'context',
'alignments' => 'stdClass[]?',
'criteria' => 'stdClass?',
);
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return array(
'alignments' => array(
'type' => alignment_exporter::read_properties_definition(),
'optional' => true,
'multiple' => true
),
'criteriaUrl' => array(
'type' => PARAM_URL,
'optional' => true
),
'criteriaNarrative' => array(
'type' => PARAM_TEXT,
'optional' => true
)
);
}
/**
* We map from related data passed as data to this exporter to clean exportable values.
*/
protected function get_other_values(renderer_base $output) {
global $DB;
$result = [];
if (array_key_exists('alignments', $this->related) && $this->related['alignments'] !== null) {
$alignments = [];
foreach ($this->related['alignments'] as $alignment) {
$exporter = new alignment_exporter($alignment, $this->related);
$alignments[] = $exporter->export($output);
}
$result['alignments'] = $alignments;
}
if (array_key_exists('criteria', $this->related) && $this->related['criteria'] !== null) {
if (property_exists($this->related['criteria'], 'id') && $this->related['criteria']->id !== null) {
$result['criteriaUrl'] = $this->related['criteria']->id;
}
if (property_exists($this->related['criteria'], 'narrative') && $this->related['criteria']->narrative !== null) {
$result['criteriaNarrative'] = $this->related['criteria']->narrative;
}
}
return $result;
}
}
+110
View File
@@ -0,0 +1,110 @@
<?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/>.
/**
* Contains class for displaying a collection.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_badges\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use stdClass;
/**
* Class for displaying a badge competency.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class collection_exporter extends exporter {
/**
* Either map version 1 data to version 2 or return it untouched.
*
* @param stdClass $data The remote data.
* @param string $apiversion The backpack version used to communicate remotely.
* @return stdClass
*/
public static function map_external_data($data, $apiversion) {
if ($apiversion == OPEN_BADGES_V1) {
$result = new stdClass();
$result->entityType = 'BackpackCollection';
$result->entityId = $data->groupId;
$result->name = $data->name;
$result->description = $data->description;
$result->assertions = [];
return $result;
}
return $data;
}
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'entityType' => [
'type' => PARAM_ALPHA,
'description' => 'BackpackCollection',
],
'entityId' => [
'type' => PARAM_RAW,
'description' => 'Unique identifier for this collection',
],
'name' => [
'type' => PARAM_TEXT,
'description' => 'Collection name',
],
'description' => [
'type' => PARAM_TEXT,
'description' => 'Collection description',
],
'share_url' => [
'type' => PARAM_URL,
'description' => 'Url to view this collection',
],
'published' => [
'type' => PARAM_BOOL,
'description' => 'True means this collection is public.',
],
'assertions' => [
'type' => PARAM_RAW,
'description' => 'List of assertion ids in this collection',
'multiple' => true,
],
];
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return array(
'context' => 'context',
);
}
}
+109
View File
@@ -0,0 +1,109 @@
<?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/>.
/**
* Contains class for displaying a issuer.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_badges\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
/**
* Class for displaying a badge competency.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class issuer_exporter extends exporter {
/**
* Either map version 1 data to version 2 or return it untouched.
*
* @param stdClass $data The remote data.
* @param string $apiversion The backpack version used to communicate remotely.
* @return stdClass
*/
public static function map_external_data($data, $apiversion) {
if ($apiversion == OPEN_BADGES_V1) {
$result = new \stdClass();
return $result;
}
$mapped = new \stdClass();
if (isset($data->entityType)) {
$mapped->type = $data->entityType;
} else {
$mapped->type = $data->type;
}
if (isset($data->entityId)) {
$mapped->id = $data->entityId;
} else {
$mapped->id = $data->id;
}
$mapped->name = $data->name;
$mapped->email = $data->email;
$mapped->url = $data->url;
return $mapped;
}
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'type' => [
'type' => PARAM_ALPHA,
'description' => 'Issuer',
],
'id' => [
'type' => PARAM_RAW,
'description' => 'Unique identifier for this issuer',
],
'name' => [
'type' => PARAM_TEXT,
'description' => 'Name of the issuer',
],
'email' => [
'type' => PARAM_EMAIL,
'description' => 'Email of the issuer',
],
'url' => [
'type' => PARAM_URL,
'description' => 'URL for this issuer',
],
];
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return array(
'context' => 'context',
);
}
}
+82
View File
@@ -0,0 +1,82 @@
<?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/>.
/**
* Contains class for displaying a recipient.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_badges\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
/**
* Class for displaying a badge competency.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class recipient_exporter extends exporter {
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'identity' => [
'type' => PARAM_RAW,
'description' => 'Hashed email address to issue badge to.',
],
'plaintextIdentity' => [
'type' => PARAM_RAW,
'description' => 'Email address to issue badge to.',
'optional' => true,
],
'salt' => [
'type' => PARAM_RAW,
'description' => 'Salt used to hash email.',
'optional' => true,
],
'type' => [
'type' => PARAM_ALPHA,
'description' => 'Email',
],
'hashed' => [
'type' => PARAM_BOOL,
'description' => 'Should be true',
],
];
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return array(
'context' => 'context',
);
}
}
+53
View File
@@ -0,0 +1,53 @@
<?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/>.
/**
* Contains class for displaying a recipient.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_badges\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
/**
* Class for displaying a badge competency.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class verification_exporter extends exporter {
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'type' => [
'type' => PARAM_ALPHA,
'description' => 'Type of verification.',
]
];
}
}
@@ -24,36 +24,43 @@
* @author Yuliya Bozhko <yuliya.bozhko@totaralms.com>
*/
namespace core_badges\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/formslib.php');
require_once($CFG->libdir . '/badgeslib.php');
use html_writer;
use moodleform;
use stdClass;
/**
* Form to edit backpack initial details.
*
*/
class edit_backpack_form extends moodleform {
class backpack extends moodleform {
/**
* Defines the form
*/
public function definition() {
global $USER, $PAGE, $OUTPUT;
global $USER, $PAGE, $OUTPUT, $CFG;
$mform = $this->_form;
$mform->addElement('html', html_writer::tag('span', '', array('class' => 'notconnected', 'id' => 'connection-error')));
$mform->addElement('header', 'backpackheader', get_string('backpackconnection', 'badges'));
$mform->addHelpButton('backpackheader', 'backpackconnection', 'badges');
$mform->addElement('static', 'url', get_string('url'), BADGE_BACKPACKURL);
$mform->addElement('hidden', 'userid', $USER->id);
$mform->setType('userid', PARAM_INT);
$sitebackpack = badges_get_site_backpack($CFG->badges_site_backpack);
if (isset($this->_customdata['email'])) {
// Email will be passed in when we're in the process of verifying the user's email address,
// so set the connection status, lock the email field, and provide options to resend the verification
// email or cancel the verification process entirely and start over.
$mform->addElement('hidden', 'backpackid', $sitebackpack->id);
$mform->setType('backpackid', PARAM_INT);
$status = html_writer::tag('span', get_string('backpackemailverificationpending', 'badges'),
array('class' => 'notconnected', 'id' => 'connection-status'));
$mform->addElement('static', 'status', get_string('status'), $status);
@@ -62,6 +69,8 @@ class edit_backpack_form extends moodleform {
$mform->hardFreeze(['email']);
$emailverify = html_writer::tag('span', s($this->_customdata['email']), []);
$mform->addElement('static', 'emailverify', get_string('email'), $emailverify);
$mform->addElement('hidden', 'backpackpassword', $this->_customdata['backpackpassword']);
$mform->setType('backpackpassword', PARAM_RAW);
$buttonarray = [];
$buttonarray[] = &$mform->createElement('submit', 'submitbutton',
get_string('backpackconnectionresendemail', 'badges'));
@@ -71,6 +80,11 @@ class edit_backpack_form extends moodleform {
$mform->closeHeaderBefore('buttonar');
} else {
// Email isn't present, so provide an input element to get it and a button to start the verification process.
$mform->addElement('static', 'info', '', $sitebackpack->backpackweburl);
$mform->addElement('hidden', 'backpackid', $sitebackpack->id);
$mform->setType('backpackid', PARAM_INT);
$status = html_writer::tag('span', get_string('notconnected', 'badges'),
array('class' => 'notconnected', 'id' => 'connection-status'));
$mform->addElement('static', 'status', get_string('status'), $status);
@@ -78,6 +92,8 @@ class edit_backpack_form extends moodleform {
$mform->addHelpButton('email', 'backpackemail', 'badges');
$mform->addRule('email', get_string('required'), 'required', null, 'client');
$mform->setType('email', PARAM_EMAIL);
$mform->addElement('passwordunmask', 'backpackpassword', get_string('password'));
$mform->setType('backpackpassword', PARAM_RAW);
$this->add_action_buttons(false, get_string('backpackconnectionconnect', 'badges'));
}
}
@@ -87,79 +103,21 @@ class edit_backpack_form extends moodleform {
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);
// We don't need to verify the email address if we're clearing a pending email verification attempt.
if (!isset($data['revertbutton'])) {
$check = new stdClass();
$check->backpackurl = BADGE_BACKPACKURL;
$backpack = badges_get_site_backpack($data['backpackid'], false);
$check->backpackurl = $backpack->backpackapiurl;
$check->apiversion = $backpack->apiversion;
$check->email = $data['email'];
$check->password = $data['backpackpassword'];
$bp = new OpenBadgesBackpackHandler($check);
$request = $bp->curl_request('user');
if (isset($request->status) && $request->status == 'missing') {
$errors['email'] = get_string('error:nosuchuser', 'badges');
} else if (!isset($request->status) || $request->status !== 'okay') {
$bp = new \core_badges\backpack_api($backpack, $check);
$result = $bp->authenticate();
if ($result === false || !empty($result->error)) {
$errors['email'] = get_string('backpackconnectionunexpectedresult', 'badges');
}
}
return $errors;
}
}
/**
* Form to select backpack collections.
*
*/
class edit_collections_form extends moodleform {
/**
* Defines the form
*/
public function definition() {
global $USER;
$mform = $this->_form;
$email = $this->_customdata['email'];
$bid = $this->_customdata['backpackid'];
$selected = $this->_customdata['selected'];
if (isset($this->_customdata['groups'])) {
$groups = $this->_customdata['groups'];
$nogroups = null;
} else {
$groups = null;
$nogroups = $this->_customdata['nogroups'];
}
$mform->addElement('header', 'backpackheader', get_string('backpackconnection', 'badges'));
$mform->addHelpButton('backpackheader', 'backpackconnection', 'badges');
$mform->addElement('static', 'url', get_string('url'), BADGE_BACKPACKURL);
$status = html_writer::tag('span', get_string('connected', 'badges'), array('class' => 'connected'));
$mform->addElement('static', 'status', get_string('status'), $status);
$mform->addElement('static', 'email', get_string('email'), $email);
$mform->addHelpButton('email', 'backpackemail', 'badges');
$mform->addElement('submit', 'disconnect', get_string('disconnect', 'badges'));
$mform->addElement('header', 'collectionheader', get_string('backpackimport', 'badges'));
$mform->addHelpButton('collectionheader', 'backpackimport', 'badges');
if (!empty($groups)) {
$mform->addElement('static', 'selectgroup', '', get_string('selectgroup_start', 'badges'));
foreach ($groups as $group) {
$name = $group->name . ' (' . $group->badges . ')';
$mform->addElement('advcheckbox', 'group[' . $group->groupId . ']', null, $name, array('group' => 1), array(false, $group->groupId));
if (in_array($group->groupId, $selected)) {
$mform->setDefault('group[' . $group->groupId . ']', $group->groupId);
}
}
$mform->addElement('static', 'selectgroup', '', get_string('selectgroup_end', 'badges'));
} else {
$mform->addElement('static', 'selectgroup', '', $nogroups);
}
$mform->addElement('hidden', 'backpackid', $bid);
$mform->setType('backpackid', PARAM_INT);
$this->add_action_buttons();
}
}
@@ -24,17 +24,21 @@
* @author Yuliya Bozhko <yuliya.bozhko@totaralms.com>
*/
namespace core_badges\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/formslib.php');
require_once($CFG->libdir . '/badgeslib.php');
require_once($CFG->libdir . '/filelib.php');
use moodleform;
/**
* Form to edit badge details.
*
*/
class edit_details_form extends moodleform {
class badge extends moodleform {
/**
* Defines the form
@@ -90,20 +94,42 @@ class edit_details_form extends moodleform {
$mform->addElement('header', 'issuerdetails', get_string('issuerdetails', 'badges'));
$mform->addElement('text', 'issuername', get_string('name'), array('size' => '70'));
$mform->setType('issuername', PARAM_NOTAGS);
$mform->addRule('issuername', null, 'required');
if (isset($CFG->badges_defaultissuername)) {
$mform->setDefault('issuername', $CFG->badges_defaultissuername);
}
$mform->addHelpButton('issuername', 'issuername', 'badges');
if (badges_open_badges_backpack_api() != OPEN_BADGES_V2) {
$mform->addElement('text', 'issuername', get_string('name'), array('size' => '70'));
$mform->setType('issuername', PARAM_NOTAGS);
$mform->addRule('issuername', null, 'required');
if (isset($CFG->badges_defaultissuername)) {
$mform->setDefault('issuername', $CFG->badges_defaultissuername);
}
$mform->addHelpButton('issuername', 'issuername', 'badges');
$mform->addElement('text', 'issuercontact', get_string('contact', 'badges'), array('size' => '70'));
if (isset($CFG->badges_defaultissuercontact)) {
$mform->setDefault('issuercontact', $CFG->badges_defaultissuercontact);
$mform->addElement('text', 'issuercontact', get_string('contact', 'badges'), array('size' => '70'));
if (isset($CFG->badges_defaultissuercontact)) {
$mform->setDefault('issuercontact', $CFG->badges_defaultissuercontact);
}
$mform->setType('issuercontact', PARAM_RAW);
$mform->addHelpButton('issuercontact', 'contact', 'badges');
// Set issuer URL.
// Have to parse URL because badge issuer origin cannot be a subfolder in wwwroot.
$url = parse_url($CFG->wwwroot);
$mform->addElement('hidden', 'issuerurl', $url['scheme'] . '://' . $url['host']);
$mform->setType('issuerurl', PARAM_URL);
} else {
$name = $CFG->badges_defaultissuername;
$mform->addElement('static', 'issuernamelabel', get_string('name'), $name);
$mform->addElement('hidden', 'issuername', $name);
$mform->setType('issuername', PARAM_NOTAGS);
$contact = $CFG->badges_defaultissuercontact;
$mform->addElement('static', 'issuercontactlabel', get_string('contact', 'badges'), $contact);
$mform->addElement('hidden', 'issuercontact', $contact);
$mform->setType('issuercontact', PARAM_RAW);
$url = parse_url($CFG->wwwroot);
$mform->addElement('hidden', 'issuerurl', $url['scheme'] . '://' . $url['host']);
$mform->setType('issuerurl', PARAM_URL);
}
$mform->setType('issuercontact', PARAM_RAW);
$mform->addHelpButton('issuercontact', 'contact', 'badges');
$mform->addElement('header', 'issuancedetails', get_string('issuancedetails', 'badges'));
@@ -127,12 +153,6 @@ class edit_details_form extends moodleform {
$mform->disabledIf('expireperiod[number]', 'expiry', 'neq', 2);
$mform->disabledIf('expireperiod[timeunit]', 'expiry', 'neq', 2);
// Set issuer URL.
// Have to parse URL because badge issuer origin cannot be a subfolder in wwwroot.
$url = parse_url($CFG->wwwroot);
$mform->addElement('hidden', 'issuerurl', $url['scheme'] . '://' . $url['host']);
$mform->setType('issuerurl', PARAM_URL);
$mform->addElement('hidden', 'action', $action);
$mform->setType('action', PARAM_TEXT);
@@ -182,8 +202,10 @@ class edit_details_form extends moodleform {
global $DB;
$errors = parent::validation($data, $files);
if (!empty($data['issuercontact']) && !validate_email($data['issuercontact'])) {
$errors['issuercontact'] = get_string('invalidemail');
if (badges_open_badges_backpack_api() != OPEN_BADGES_V2) {
if (!empty($data['issuercontact']) && !validate_email($data['issuercontact'])) {
$errors['issuercontact'] = get_string('invalidemail');
}
}
if ($data['expiry'] == 2 && $data['expireperiod'] <= 0) {
@@ -219,61 +241,3 @@ class edit_details_form extends moodleform {
}
}
/**
* Form to edit badge message.
*
*/
class edit_message_form extends moodleform {
public function definition() {
global $CFG, $OUTPUT;
$mform = $this->_form;
$badge = $this->_customdata['badge'];
$action = $this->_customdata['action'];
$editoroptions = $this->_customdata['editoroptions'];
// Add hidden fields.
$mform->addElement('hidden', 'id', $badge->id);
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'action', $action);
$mform->setType('action', PARAM_TEXT);
$mform->addElement('header', 'badgemessage', get_string('configuremessage', 'badges'));
$mform->addHelpButton('badgemessage', 'variablesubstitution', 'badges');
$mform->addElement('text', 'messagesubject', get_string('subject', 'badges'), array('size' => '70'));
$mform->setType('messagesubject', PARAM_TEXT);
$mform->addRule('messagesubject', null, 'required');
$mform->addRule('messagesubject', get_string('maximumchars', '', 255), 'maxlength', 255);
$mform->addElement('editor', 'message_editor', get_string('message', 'badges'), null, $editoroptions);
$mform->setType('message_editor', PARAM_RAW);
$mform->addRule('message_editor', null, 'required');
$mform->addElement('advcheckbox', 'attachment', get_string('attachment', 'badges'), '', null, array(0, 1));
$mform->addHelpButton('attachment', 'attachment', 'badges');
if (empty($CFG->allowattachments)) {
$mform->freeze('attachment');
}
$options = array(
BADGE_MESSAGE_NEVER => get_string('never'),
BADGE_MESSAGE_ALWAYS => get_string('notifyevery', 'badges'),
BADGE_MESSAGE_DAILY => get_string('notifydaily', 'badges'),
BADGE_MESSAGE_WEEKLY => get_string('notifyweekly', 'badges'),
BADGE_MESSAGE_MONTHLY => get_string('notifymonthly', 'badges'),
);
$mform->addElement('select', 'notification', get_string('notification', 'badges'), $options);
$mform->addHelpButton('notification', 'notification', 'badges');
$this->add_action_buttons();
$this->set_data($badge);
}
public function validation($data, $files) {
$errors = parent::validation($data, $files);
return $errors;
}
}
+109
View File
@@ -0,0 +1,109 @@
<?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/>.
/**
* Form class for mybackpack.php
*
* @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 <[email protected]>
*/
namespace core_badges\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/formslib.php');
require_once($CFG->libdir . '/badgeslib.php');
use html_writer;
use moodleform;
/**
* Form to select backpack collections.
*
*/
class collections extends moodleform {
/**
* Defines the form
*/
public function definition() {
global $USER;
$mform = $this->_form;
$email = $this->_customdata['email'];
$backpackweburl = $this->_customdata['backpackweburl'];
$selected = $this->_customdata['selected'];
if (isset($this->_customdata['groups'])) {
$groups = $this->_customdata['groups'];
$nogroups = null;
} else {
$groups = null;
$nogroups = $this->_customdata['nogroups'];
}
$backpack = get_backpack_settings($USER->id);
$mform->addElement('header', 'backpackheader', get_string('backpackconnection', 'badges'));
$mform->addHelpButton('backpackheader', 'backpackconnection', 'badges');
$mform->addElement('static', 'url', get_string('url'), $backpackweburl);
$status = html_writer::tag('span', get_string('connected', 'badges'), array('class' => 'connected'));
$mform->addElement('static', 'status', get_string('status'), $status);
$mform->addElement('static', 'email', get_string('email'), $email);
$mform->addHelpButton('email', 'backpackemail', 'badges');
$mform->addElement('submit', 'disconnect', get_string('disconnect', 'badges'));
$mform->addElement('header', 'collectionheader', get_string('backpackimport', 'badges'));
$mform->addHelpButton('collectionheader', 'backpackimport', 'badges');
if (!empty($groups)) {
$mform->addElement('static', 'selectgroup', '', get_string('selectgroup_start', 'badges'));
foreach ($groups as $group) {
// Assertions or badges.
$count = 0;
if (!empty($group->assertions)) {
$count = count($group->assertions);
}
if (!empty($group->badges)) {
$count = count($group->badges);
}
if (!empty($group->groupId)) {
$group->entityId = $group->groupId;
}
$name = $group->name . ' (' . $count . ')';
$mform->addElement(
'advcheckbox',
'group[' . $group->entityId . ']',
null,
$name,
array('group' => 1),
array(false, $group->entityId)
);
if (in_array($group->entityId, $selected)) {
$mform->setDefault('group[' . $group->entityId . ']', $group->entityId);
}
}
$mform->addElement('static', 'selectgroup', '', get_string('selectgroup_end', 'badges', $backpackweburl));
} else {
$mform->addElement('static', 'selectgroup', '', $nogroups);
}
$this->add_action_buttons();
}
}
+94
View File
@@ -0,0 +1,94 @@
<?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/>.
/**
* External backpack form
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_badges\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
/**
* Backpack form class.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class external_backpack extends \moodleform {
public function definition() {
global $CFG;
$mform = $this->_form;
$backpack = false;
if (isset($this->_customdata['externalbackpack'])) {
$backpack = $this->_customdata['externalbackpack'];
} else {
throw new \coding_exception('backpack is required.');
}
$url = $backpack->backpackapiurl;
$mform->addElement('static', 'backpackapiurlinfo', get_string('backpackapiurl', 'core_badges'), $url);
$mform->addElement('hidden', 'backpackapiurl', $url);
$mform->setType('backpackapiurl', PARAM_URL);
$url = $backpack->backpackweburl;
$mform->addElement('static', 'backpackweburlinfo', get_string('backpackweburl', 'core_badges'), $url);
$mform->addElement('hidden', 'backpackweburl', $url);
$mform->setType('backpackweburl', PARAM_URL);
$options = badges_get_badge_api_versions();
$label = $options[$backpack->apiversion];
$mform->addElement('static', 'apiversioninfo', get_string('apiversion', 'core_badges'), $label);
$mform->addElement('hidden', 'apiversion', $backpack->apiversion);
$mform->setType('apiversion', PARAM_INTEGER);
$mform->addElement('hidden', 'id', $backpack->id);
$mform->setType('id', PARAM_INTEGER);
$mform->addElement('hidden', 'action', 'edit');
$mform->setType('action', PARAM_ALPHA);
$issuername = $CFG->badges_defaultissuername;
$mform->addElement('static', 'issuerinfo', get_string('defaultissuername', 'core_badges'), $issuername);
$issuercontact = $CFG->badges_defaultissuercontact;
$mform->addElement('static', 'issuerinfo', get_string('defaultissuercontact', 'core_badges'), $issuercontact);
$mform->addElement('passwordunmask', 'password', get_string('defaultissuerpassword', 'core_badges'));
$mform->setType('password', PARAM_RAW);
$mform->addHelpButton('password', 'defaultissuerpassword', 'badges');
$mform->hideIf('password', 'apiversion', 'eq', 1);
$this->set_data($backpack);
// Disable short forms.
$mform->setDisableShortforms();
$this->add_action_buttons();
}
}
+93
View File
@@ -0,0 +1,93 @@
<?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/>.
/**
* Form class for badge message.
*
* @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 <[email protected]>
*/
namespace core_badges\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/formslib.php');
require_once($CFG->libdir . '/badgeslib.php');
require_once($CFG->libdir . '/filelib.php');
use moodleform;
/**
* Form to edit badge message.
*/
class message extends moodleform {
public function definition() {
global $CFG, $OUTPUT;
$mform = $this->_form;
$badge = $this->_customdata['badge'];
$action = $this->_customdata['action'];
$editoroptions = $this->_customdata['editoroptions'];
// Add hidden fields.
$mform->addElement('hidden', 'id', $badge->id);
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'action', $action);
$mform->setType('action', PARAM_TEXT);
$mform->addElement('header', 'badgemessage', get_string('configuremessage', 'badges'));
$mform->addHelpButton('badgemessage', 'variablesubstitution', 'badges');
$mform->addElement('text', 'messagesubject', get_string('subject', 'badges'), array('size' => '70'));
$mform->setType('messagesubject', PARAM_TEXT);
$mform->addRule('messagesubject', null, 'required');
$mform->addRule('messagesubject', get_string('maximumchars', '', 255), 'maxlength', 255);
$mform->addElement('editor', 'message_editor', get_string('message', 'badges'), null, $editoroptions);
$mform->setType('message_editor', PARAM_RAW);
$mform->addRule('message_editor', null, 'required');
$mform->addElement('advcheckbox', 'attachment', get_string('attachment', 'badges'), '', null, array(0, 1));
$mform->addHelpButton('attachment', 'attachment', 'badges');
if (empty($CFG->allowattachments)) {
$mform->freeze('attachment');
}
$options = array(
BADGE_MESSAGE_NEVER => get_string('never'),
BADGE_MESSAGE_ALWAYS => get_string('notifyevery', 'badges'),
BADGE_MESSAGE_DAILY => get_string('notifydaily', 'badges'),
BADGE_MESSAGE_WEEKLY => get_string('notifyweekly', 'badges'),
BADGE_MESSAGE_MONTHLY => get_string('notifymonthly', 'badges'),
);
$mform->addElement('select', 'notification', get_string('notification', 'badges'), $options);
$mform->addHelpButton('notification', 'notification', 'badges');
$this->add_action_buttons();
$this->set_data($badge);
}
public function validation($data, $files) {
$errors = parent::validation($data, $files);
return $errors;
}
}
+4
View File
@@ -24,6 +24,7 @@
defined('MOODLE_INTERNAL') || die();
use \core_badges\badge;
/**
* Event observer for badges.
*/
@@ -87,6 +88,9 @@ class core_badges_observer {
$ucid = $event->other['usercompetencyid'];
$cid = $event->other['competencyid'];
$userid = $event->relateduserid;
if (!$eventdata) {
return;
}
if ($rs = $DB->get_records('badge_criteria_param', array('name' => 'competency_' . $cid, 'value' => $cid))) {
foreach ($rs as $r) {
@@ -0,0 +1,74 @@
<?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/>.
/**
* Issued badge renderable.
*
* @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 <[email protected]>
*/
namespace core_badges\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/badgeslib.php');
use renderable;
/**
*
* @copyright 2018 Tung Thai
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Tung Thai <[email protected]>
*/
class badge_alignments implements renderable
{
/** @var string how are the data sorted. */
public $sort = 'name';
/** @var string how are the data sorted. */
public $dir = 'ASC';
/** @var int page number to display. */
public $page = 0;
/** @var int number of badges to display per page. */
public $perpage = BADGE_PERPAGE;
/** @var int the total number of badges to display. */
public $totalcount = null;
/** @var array list of badges. */
public $alignments = array();
/** @var array list of badges. */
public $currentbadgeid = 0;
/**
* Initializes the list of alignments to display.
*
* @param array $alignments List alignments to render.
* @param int $currentbadgeid ID current badge.
*/
public function __construct($alignments, $currentbadgeid) {
$this->alignments = $alignments;
$this->currentbadgeid = $currentbadgeid;
}
}
@@ -0,0 +1,67 @@
<?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/>.
/**
* Issued badge renderable.
*
* @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 <[email protected]>
*/
namespace core_badges\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/badgeslib.php');
use renderable;
/**
* Collection of all badges for view.php page
*/
class badge_collection implements renderable {
/** @var string how are the data sorted */
public $sort = 'name';
/** @var string how are the data sorted */
public $dir = 'ASC';
/** @var int page number to display */
public $page = 0;
/** @var int number of badges to display per page */
public $perpage = BADGE_PERPAGE;
/** @var int the total number of badges to display */
public $totalcount = null;
/** @var array list of badges */
public $badges = array();
/**
* Initializes the list of badges to display
*
* @param array $badges Badges to render
*/
public function __construct($badges) {
$this->badges = $badges;
}
}
@@ -0,0 +1,40 @@
<?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/>.
/**
* Issued badge renderable.
*
* @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 <[email protected]>
*/
namespace core_badges\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/badgeslib.php');
use renderable;
/**
* Collection of badges used at the index.php page
*/
class badge_management extends badge_collection implements renderable {
}
@@ -0,0 +1,67 @@
<?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/>.
/**
* Issued badge renderable.
*
* @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 <[email protected]>
*/
namespace core_badges\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/badgeslib.php');
use renderable;
/**
* Badge recipients rendering class
*/
class badge_recipients implements renderable {
/** @var string how are the data sorted */
public $sort = 'lastname';
/** @var string how are the data sorted */
public $dir = 'ASC';
/** @var int page number to display */
public $page = 0;
/** @var int number of badge recipients to display per page */
public $perpage = 30;
/** @var int the total number or badge recipients to display */
public $totalcount = null;
/** @var array internal list of badge recipients ids */
public $userids = array();
/**
* Initializes the list of users to display
*
* @param array $holders List of badge holders
*/
public function __construct($holders) {
$this->userids = $holders;
}
}
+76
View File
@@ -0,0 +1,76 @@
<?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/>.
/**
* Collection of all related badges.
*
* @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 <[email protected]>
*/
namespace core_badges\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/badgeslib.php');
use renderable;
/**
* Collection of all related badges.
*
* @copyright 2018 Tung Thai
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Tung Thai <[email protected]>
*/
class badge_related implements renderable {
/** @var string how are the data sorted. */
public $sort = 'name';
/** @var string how are the data sorted. */
public $dir = 'ASC';
/** @var int page number to display. */
public $page = 0;
/** @var int number of badges to display per page. */
public $perpage = BADGE_PERPAGE;
/** @var int the total number of badges to display. */
public $totalcount = null;
/** @var int the current badge. */
public $currentbadgeid = 0;
/** @var array list of badges. */
public $badges = array();
/**
* Initializes the list of badges to display.
*
* @param array $badges related badges to render.
* @param int $currentbadgeid ID current badge.
*/
public function __construct($badges, $currentbadgeid) {
$this->badges = $badges;
$this->currentbadgeid = $currentbadgeid;
}
}
@@ -0,0 +1,60 @@
<?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/>.
/**
* Collection of use badges.
*
* @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 <[email protected]>
*/
namespace core_badges\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/badgeslib.php');
use renderable;
/**
* Collection of user badges used at the mybadges.php page
*/
class badge_user_collection extends badge_collection implements renderable {
/** @var array backpack settings */
public $backpack = null;
/** @var string search */
public $search = '';
/**
* Initializes user badge collection.
*
* @param array $badges Badges to render
* @param int $userid Badges owner
*/
public function __construct($badges, $userid) {
global $CFG;
parent::__construct($badges);
if (!empty($CFG->badges_allowexternalbackpack)) {
$this->backpack = get_backpack_settings($userid, true);
}
}
}
@@ -0,0 +1,76 @@
<?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/>.
/**
* Manage enabled backpacks for the site.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_badges\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/badgeslib.php');
use core_badges\external\backpack_exporter;
/**
* Manage enabled backpacks renderable.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class external_backpacks_page implements \renderable {
/**
* Constructor.
* @param \moodle_url $url
*/
public function __construct(\moodle_url $url) {
$this->url = $url;
$this->backpacks = badges_get_site_backpacks();
}
/**
* 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) {
$data = new \stdClass();
$data->baseurl = $this->url;
$data->backpacks = array();
$data->sesskey = sesskey();
foreach ($this->backpacks as $backpack) {
$exporter = new backpack_exporter($backpack);
$backpack = $exporter->export($output);
if ($backpack->apiversion == OPEN_BADGES_V2) {
$backpack->canedit = true;
} else {
$backpack->canedit = false;
}
$data->backpacks[] = $backpack;
}
return $data;
}
}
@@ -0,0 +1,87 @@
<?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/>.
/**
* List of enabled backpacks for the site.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_badges\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/tablelib.php');
require_once($CFG->libdir . '/badgeslib.php');
use html_writer;
use moodle_url;
use table_sql;
/**
* Backpacks table class.
*
* @package core_badges
* @copyright 2019 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class external_backpacks_table extends table_sql {
/**
* Sets up the table.
*
* @param string $uniqueid Unique id of table.
* @param \core_competency\template $template The template.
*/
public function __construct() {
parent::__construct('backpacks');
$this->context = \context_system::instance();
// This object should not be used without the right permissions.
require_capability('moodle/badges:manageglobalsettings', $this->context);
// Define columns in the table.
$this->define_table_columns();
// Define configs.
$this->define_table_configs();
}
/**
* Setup the headers for the table.
*/
protected function define_table_columns() {
$cols = [
'backpackweburl' => get_string('backpackurl', 'core_badges'),
'sortorder' => '',
];
$this->define_columns(array_keys($cols));
$this->define_headers(array_values($cols));
}
/**
* Define table configs.
*/
protected function define_table_configs() {
$this->collapsible(false);
$this->sortable(false);
$this->pageable(false);
}
}
+97
View File
@@ -0,0 +1,97 @@
<?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/>.
/**
* External badge renderable.
*
* @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 <[email protected]>
*/
namespace core_badges\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/badgeslib.php');
use renderable;
/**
* An external badges for external.php page
*/
class external_badge implements renderable {
/** @var issued badge */
public $issued;
/** @var User ID */
public $recipient;
/** @var validation of external badge */
public $valid = true;
/**
* Initializes the badge to display
*
* @param object $badge External badge information.
* @param int $recipient User id.
*/
public function __construct($badge, $recipient) {
global $DB;
// At this point a user has connected a backpack. So, we are going to get
// their backpack email rather than their account email.
$namefields = get_all_user_name_fields(true, 'u');
$user = $DB->get_record_sql("SELECT {$namefields}, b.email
FROM {user} u INNER JOIN {badge_backpack} b ON u.id = b.userid
WHERE b.userid = :userid", array('userid' => $recipient), IGNORE_MISSING);
$this->issued = $badge;
$this->recipient = $user;
// Check if recipient is valid.
// There is no way to be 100% sure that a badge belongs to a user.
// Backpack does not return any recipient information.
// All we can do is compare that backpack email hashed using salt
// provided in the assertion matches a badge recipient from the assertion.
if ($user) {
if (isset($badge->assertion->recipient->identity)) {
$badge->assertion->salt = $badge->assertion->recipient->salt;
$badge->assertion->recipient = $badge->assertion->recipient->identity;
}
// Open Badges V2 does not even include a recipient.
if (!isset($badge->assertion->recipient)) {
$this->valid = false;
} else if (validate_email($badge->assertion->recipient) && $badge->assertion->recipient == $user->email) {
// If we have email, compare emails.
$this->valid = true;
} else if ($badge->assertion->recipient == 'sha256$' . hash('sha256', $user->email)) {
// If recipient is hashed, but no salt, compare hashes without salt.
$this->valid = true;
} else if ($badge->assertion->recipient == 'sha256$' . hash('sha256', $user->email . $badge->assertion->salt)) {
// If recipient is hashed, compare hashes.
$this->valid = true;
} else {
// Otherwise, we cannot be sure that this user is a recipient.
$this->valid = false;
}
} else {
$this->valid = false;
}
}
}
+81
View File
@@ -0,0 +1,81 @@
<?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/>.
/**
* Issued badge renderable.
*
* @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 <[email protected]>
*/
namespace core_badges\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/badgeslib.php');
use renderable;
/**
* An issued badges for badge.php page
*/
class issued_badge implements renderable {
/** @var issued badge */
public $issued;
/** @var badge recipient */
public $recipient;
/** @var badge class */
public $badgeclass;
/** @var badge visibility to others */
public $visible = 0;
/** @var badge class */
public $badgeid = 0;
/**
* Initializes the badge to display
*
* @param string $hash Issued badge hash
*/
public function __construct($hash) {
global $DB;
$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}
WHERE ' . $DB->sql_compare_text('uniquehash', 40) . ' = ' . $DB->sql_compare_text(':hash', 40),
array('hash' => $hash), IGNORE_MISSING);
if ($rec) {
// Get a recipient from database.
$namefields = get_all_user_name_fields(true, 'u');
$user = $DB->get_record_sql("SELECT u.id, $namefields, u.deleted, u.email
FROM {user} u WHERE u.id = :userid", array('userid' => $rec->userid));
$this->recipient = $user;
$this->visible = $rec->visible;
$this->badgeid = $rec->badgeid;
}
}
}
+5 -5
View File
@@ -26,10 +26,10 @@
require_once(__DIR__ . '/../config.php');
require_once($CFG->libdir . '/badgeslib.php');
require_once($CFG->dirroot . '/badges/edit_form.php');
require_once($CFG->libdir . '/filelib.php');
$badgeid = required_param('id', PARAM_INT);
$action = optional_param('action', 'details', PARAM_TEXT);
$action = optional_param('action', 'badge', PARAM_TEXT);
require_login();
@@ -84,13 +84,13 @@ $editoroptions = array(
);
$badge = file_prepare_standard_editor($badge, 'message', $editoroptions, $context);
$formclass = 'edit_' . $action . '_form';
$formclass = '\core_badges\form' . '\\' . $action;
$form = new $formclass($currenturl, array('badge' => $badge, 'action' => $action, 'editoroptions' => $editoroptions));
if ($form->is_cancelled()) {
redirect(new moodle_url('/badges/overview.php', array('id' => $badgeid)));
} else if ($form->is_submitted() && $form->is_validated() && ($data = $form->get_data())) {
if ($action == 'details') {
if ($action == 'badge') {
$badge->name = $data->name;
$badge->version = trim($data->version);
$badge->language = $data->language;
@@ -157,4 +157,4 @@ $output->print_badge_tabs($badgeid, $context, $action);
$form->display();
echo $OUTPUT->footer();
echo $OUTPUT->footer();
+11 -4
View File
@@ -37,6 +37,7 @@ $hash = required_param('hash', PARAM_ALPHANUM);
$userid = required_param('user', PARAM_INT);
$PAGE->set_url(new moodle_url('/badges/external.php', array('hash' => $hash, 'user' => $userid)));
$PAGE->set_context(context_system::instance());
// Using the same setting as user profile page.
if (!empty($CFG->forceloginforprofiles)) {
@@ -75,15 +76,21 @@ if (empty($badge)) {
print_error('error:externalbadgedoesntexist', 'badges');
}
$PAGE->set_context(context_system::instance());
$output = $PAGE->get_renderer('core', 'badges');
$badge = new external_badge($badge, $userid);
$badge = new \core_badges\output\external_badge($badge, $userid);
$PAGE->set_pagelayout('base');
$PAGE->set_title(get_string('issuedbadge', 'badges'));
$PAGE->set_heading(s($badge->issued->assertion->badge->name));
$PAGE->navbar->add(s($badge->issued->assertion->badge->name));
$badgename = '';
if (!empty($badge->issued->name)) {
$badgename = s($badge->issued->name);
}
if (!empty($badge->issued->assertion->badge->name)) {
$badgename = s($badge->issued->assertion->badge->name);
}
$PAGE->set_heading($badgename);
$PAGE->navbar->add($badgename);
if (isloggedin() && $USER->id == $userid) {
$url = new moodle_url('/badges/mybadges.php');
} else {
+4 -5
View File
@@ -104,8 +104,7 @@ if (!has_any_capability(array(
}
$PAGE->set_title($hdr);
$PAGE->requires->js('/badges/backpack.js');
$PAGE->requires->js_init_call('check_site_access', null, false);
badges_local_backpack_js(true);
$output = $PAGE->get_renderer('core', 'badges');
if (($delete || $archive) && has_capability('moodle/badges:deletebadge', $PAGE->context)) {
@@ -179,7 +178,7 @@ if ($totalcount) {
echo $OUTPUT->notification(get_string($msg, 'badges'), 'notifysuccess');
}
$badges = new badge_management($records);
$badges = new \core_badges\output\badge_management($records);
$badges->sort = $sortby;
$badges->dir = $sorthow;
$badges->page = $page;
@@ -191,8 +190,8 @@ if ($totalcount) {
echo $output->notification(get_string('nobadges', 'badges'));
if (has_capability('moodle/badges:createbadge', $PAGE->context)) {
echo $OUTPUT->single_button(new moodle_url('newbadge.php', array('type' => $type, 'id' => $courseid)),
get_string('newbadge', 'badges'));
echo $OUTPUT->box($OUTPUT->single_button(new moodle_url('newbadge.php', array('type' => $type, 'id' => $courseid)),
get_string('newbadge', 'badges')));
}
}
-161
View File
@@ -1,161 +0,0 @@
<?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/>.
/**
* External backpack 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 <[email protected]>
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/filelib.php');
// Adopted from https://github.com/jbkc85/openbadges-class-php.
// Author Jason Cameron <[email protected]>.
class OpenBadgesBackpackHandler {
private $backpack;
private $email;
private $backpackuid = 0;
public function __construct($record) {
$this->backpack = $record->backpackurl;
$this->email = $record->email;
$this->backpackuid = isset($record->backpackuid) ? $record->backpackuid : 0;
}
public function curl_request($action, $collection = null) {
$curl = new curl();
switch($action) {
case 'user':
$url = $this->backpack . "/displayer/convert/email";
$param = array('email' => $this->email);
break;
case 'groups':
$url = $this->backpack . '/displayer/' . $this->backpackuid . '/groups.json';
break;
case 'badges':
$url = $this->backpack . '/displayer/' . $this->backpackuid . '/group/' . $collection . '.json';
break;
}
$curl->setHeader(array('Accept: application/json', 'Expect:'));
$options = array(
'FRESH_CONNECT' => true,
'RETURNTRANSFER' => true,
'FORBID_REUSE' => true,
'HEADER' => 0,
'CONNECTTIMEOUT' => 3,
// Follow redirects with the same type of request when sent 301, or 302 redirects.
'CURLOPT_POSTREDIR' => 3
);
if ($action == 'user') {
$out = $curl->post($url, $param, $options);
} else {
$out = $curl->get($url, array(), $options);
}
return json_decode($out);
}
private function check_status($status) {
switch($status) {
case "missing":
$response = array(
'status' => $status,
'message' => get_string('error:nosuchuser', 'badges')
);
return $response;
}
}
public function get_collections() {
$json = $this->curl_request('user', $this->email);
if (isset($json->status)) {
if ($json->status != 'okay') {
return $this->check_status($json->status);
} else {
$this->backpackuid = $json->userId;
return $this->curl_request('groups');
}
}
}
public function get_badges($collection) {
$json = $this->curl_request('user', $this->email);
if (isset($json->status)) {
if ($json->status != 'okay') {
return $this->check_status($json->status);
} else {
return $this->curl_request('badges', $collection);
}
}
}
public function get_url() {
return $this->backpack;
}
}
/**
* Create and send a verification email to the email address supplied.
*
* Since we're not sending this email to a user, email_to_user can't be used
* but this function borrows largely the code from that process.
*
* @param string $email the email address to send the verification email to.
* @return true if the email was sent successfully, false otherwise.
*/
function send_verification_email($email) {
global $DB, $USER;
// Store a user secret (badges_email_verify_secret) and the address (badges_email_verify_address) as users prefs.
// The address will be used by edit_backpack_form for display during verification and to facilitate the resending
// of verification emails to said address.
$secret = random_string(15);
set_user_preference('badges_email_verify_secret', $secret);
set_user_preference('badges_email_verify_address', $email);
// To, from.
$tempuser = $DB->get_record('user', array('id' => $USER->id), '*', MUST_EXIST);
$tempuser->email = $email;
$noreplyuser = core_user::get_noreply_user();
// Generate the verification email body.
$verificationurl = '/badges/backpackemailverify.php';
$verificationurl = new moodle_url($verificationurl);
$verificationpath = $verificationurl->out(false);
$site = get_site();
$args = new stdClass();
$args->link = $verificationpath . '?data='. $secret;
$args->sitename = $site->fullname;
$args->admin = generate_email_signoff();
$messagesubject = get_string('backpackemailverifyemailsubject', 'badges', $site->fullname);
$messagetext = get_string('backpackemailverifyemailbody', 'badges', $args);
$messagehtml = text_to_html($messagetext, false, false, true);
return email_to_user($tempuser, $noreplyuser, $messagesubject, $messagetext, $messagehtml);
}
+27 -38
View File
@@ -26,8 +26,6 @@
require_once(__DIR__ . '/../config.php');
require_once($CFG->libdir . '/badgeslib.php');
require_once($CFG->dirroot . '/badges/backpack_form.php');
require_once($CFG->dirroot . '/badges/lib/backpacklib.php');
require_login();
@@ -57,25 +55,34 @@ $badgescache = cache::make('core', 'externalbadges');
if ($disconnect && $backpack) {
require_sesskey();
$DB->delete_records('badge_external', array('backpackid' => $backpack->id));
$DB->delete_records('badge_backpack', array('userid' => $USER->id));
$badgescache->delete($USER->id);
$sitebackpack = badges_get_site_backpack(0, $backpack->backpackurl);
// If backpack is connected, need to select collections.
$bp = new \core_badges\backpack_api($sitebackpack, $backpack);
$bp->disconnect_backpack($USER->id, $backpack->id);
redirect(new moodle_url('/badges/mybackpack.php'));
}
if ($backpack) {
$sitebackpack = badges_get_site_backpack(0, $backpack->backpackurl);
// If backpack is connected, need to select collections.
$bp = new OpenBadgesBackpackHandler($backpack);
$bp = new \core_badges\backpack_api($sitebackpack, $backpack);
$request = $bp->get_collections();
if (empty($request->groups)) {
$params['nogroups'] = get_string('error:nogroups', 'badges');
$groups = $request;
if (isset($request->groups)) {
$groups = $request->groups;
}
if (empty($groups)) {
$err = get_string('error:nogroupssummary', 'badges');
$err .= get_string('error:nogroupslink', 'badges', $sitebackpack->backpackweburl);
$params['nogroups'] = $err;
} else {
$params['groups'] = $request->groups;
$params['groups'] = $groups;
}
$params['email'] = $backpack->email;
$params['selected'] = $DB->get_fieldset_select('badge_external', 'collectionid', 'backpackid = :bid', array('bid' => $backpack->id));
$params['backpackid'] = $backpack->id;
$form = new edit_collections_form(new moodle_url('/badges/mybackpack.php'), $params);
$params['selected'] = $bp->get_collection_record($backpack->id);
$params['backpackweburl'] = $sitebackpack->backpackweburl;
$form = new \core_badges\form\collections(new moodle_url('/badges/mybackpack.php'), $params);
if ($form->is_cancelled()) {
redirect(new moodle_url('/badges/mybadges.php'));
@@ -85,27 +92,7 @@ if ($backpack) {
} else {
$groups = array_filter($data->group);
}
// Remove all unselected collections if there are any.
$sqlparams = array('backpack' => $backpack->id);
$select = 'backpackid = :backpack ';
if (!empty($groups)) {
list($grouptest, $groupparams) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'col', false);
$select .= ' AND collectionid ' . $grouptest;
$sqlparams = array_merge($sqlparams, $groupparams);
}
$DB->delete_records_select('badge_external', $select, $sqlparams);
// Insert selected collections if they are not in database yet.
foreach ($groups as $group) {
$obj = new stdClass();
$obj->backpackid = $data->backpackid;
$obj->collectionid = (int) $group;
if (!$DB->record_exists('badge_external', array('backpackid' => $obj->backpackid, 'collectionid' => $obj->collectionid))) {
$DB->insert_record('badge_external', $obj);
}
}
$badgescache->delete($USER->id);
$bp->set_backpack_collections($backpack->id, $groups);
redirect(new moodle_url('/badges/mybadges.php'));
}
} else {
@@ -121,8 +108,10 @@ if ($backpack) {
// To pass through the current state of the verification attempt to the form.
$params['email'] = get_user_preferences('badges_email_verify_address');
$params['backpackpassword'] = get_user_preferences('badges_email_verify_password');
$params['backpackid'] = get_user_preferences('badges_email_verify_backpackid');
$form = new edit_backpack_form(new moodle_url('/badges/mybackpack.php'), $params);
$form = new \core_badges\form\backpack(new moodle_url('/badges/mybackpack.php'), $params);
if ($form->is_cancelled()) {
redirect(new moodle_url('/badges/mybadges.php'));
} else if ($data = $form->get_data()) {
@@ -131,11 +120,11 @@ if ($backpack) {
// 2. After clicking 'Resend verification email'. We'll have $data->email.
// 3. After clicking 'Connect using a different email' to cancel the verification process. We'll have $data->revertbutton.
if (isset($data->revertbutton)) {
unset_user_preference('badges_email_verify_secret');
unset_user_preference('badges_email_verify_address');
badges_disconnect_user_backpack($USER->id);
redirect(new moodle_url('/badges/mybackpack.php'));
} else if (isset($data->email)) {
if (send_verification_email($data->email)) {
if (badges_send_verification_email($data->email, $data->backpackid, $data->backpackpassword)) {
$a = get_user_preferences('badges_email_verify_backpackid');
redirect(new moodle_url('/badges/mybackpack.php'),
get_string('backpackemailverifypending', 'badges', $data->email),
null, \core\output\notification::NOTIFY_INFO);
+8 -1
View File
@@ -99,10 +99,17 @@ $output = $PAGE->get_renderer('core', 'badges');
$badges = badges_get_user_badges($USER->id);
echo $OUTPUT->header();
$success = optional_param('success', '', PARAM_ALPHA);
$warning = optional_param('warning', '', PARAM_ALPHA);
if (!empty($success)) {
echo $OUTPUT->notification(get_string($success, 'core_badges'), 'notifysuccess');
} else if (!empty($warning)) {
echo $OUTPUT->notification(get_string($warning, 'core_badges'), 'warning');
}
$totalcount = count($badges);
$records = badges_get_user_badges($USER->id, null, $page, BADGE_PERPAGE, $search);
$userbadges = new badge_user_collection($records, $USER->id);
$userbadges = new \core_badges\output\badge_user_collection($records, $USER->id);
$userbadges->sort = 'dateissued';
$userbadges->dir = 'DESC';
$userbadges->page = $page;
+15 -8
View File
@@ -26,7 +26,6 @@
require_once(__DIR__ . '/../config.php');
require_once($CFG->libdir . '/badgeslib.php');
require_once($CFG->dirroot . '/badges/edit_form.php');
$type = required_param('type', PARAM_INT);
$courseid = optional_param('id', 0, PARAM_INT);
@@ -62,13 +61,12 @@ if (($type == BADGE_TYPE_COURSE) && ($course = $DB->get_record('course', array('
require_capability('moodle/badges:createbadge', $PAGE->context);
$PAGE->requires->js('/badges/backpack.js');
$PAGE->requires->js_init_call('check_site_access', null, false);
badges_local_backpack_js(true);
$fordb = new stdClass();
$fordb->id = null;
$form = new edit_details_form($PAGE->url, array('action' => 'new'));
$form = new \core_badges\form\badge($PAGE->url, array('action' => 'new'));
if ($form->is_cancelled()) {
redirect(new moodle_url('/badges/index.php', array('type' => $type, 'id' => $courseid)));
@@ -88,9 +86,18 @@ if ($form->is_cancelled()) {
$fordb->timemodified = $now;
$fordb->usercreated = $USER->id;
$fordb->usermodified = $USER->id;
$fordb->issuername = $data->issuername;
$fordb->issuerurl = $data->issuerurl;
$fordb->issuercontact = $data->issuercontact;
if (badges_open_badges_backpack_api() != OPEN_BADGES_V2) {
$fordb->issuername = $data->issuername;
$fordb->issuerurl = $data->issuerurl;
$fordb->issuercontact = $data->issuercontact;
} else {
$url = parse_url($CFG->wwwroot);
$fordb->issuerurl = $url['scheme'] . '://' . $url['host'];
$fordb->issuername = $CFG->badges_defaultissuername;
$fordb->issuercontact = $CFG->badges_defaultissuercontact;
}
$fordb->expiredate = ($data->expiry == 1) ? $data->expiredate : null;
$fordb->expireperiod = ($data->expiry == 2) ? $data->expireperiod : null;
$fordb->type = $type;
@@ -123,4 +130,4 @@ echo $OUTPUT->box('', 'notifyproblem hide', 'check_connection');
$form->display();
echo $OUTPUT->footer();
echo $OUTPUT->footer();
+2 -2
View File
@@ -98,7 +98,7 @@ $totalcount = $DB->count_records('badge_issued', array('badgeid' => $badge->id))
if ($badge->has_awards()) {
$users = $DB->get_records_sql($sql, array('badgeid' => $badge->id), $page * BADGE_PERPAGE, BADGE_PERPAGE);
$recipients = new badge_recipients($users);
$recipients = new core_badges\output\badge_recipients($users);
$recipients->sort = $sortby;
$recipients->dir = $sorthow;
$recipients->page = $page;
@@ -110,4 +110,4 @@ if ($badge->has_awards()) {
echo $output->notification(get_string('noawards', 'badges'));
}
echo $output->footer();
echo $output->footer();
+1 -1
View File
@@ -95,7 +95,7 @@ if (is_null($action)) {
}
if ($badge->has_related()) {
$badgerelated = $badge->get_related_badges();
$renderrelated = new badge_related($badgerelated, $badgeid);
$renderrelated = new \core_badges\output\badge_related($badgerelated, $badgeid);
echo $output->render($renderrelated);
} else {
echo $output->notification(get_string('norelated', 'badges'));
+115 -319
View File
@@ -41,8 +41,20 @@ class core_badges_renderer extends plugin_renderer_base {
$bname = $badge->name;
$imageurl = moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $badge->id, '/', 'f1', false);
} else {
$bname = s($badge->assertion->badge->name);
$imageurl = $badge->imageUrl;
$bname = '';
$imageurl = '';
if (!empty($badge->name)) {
$bname = s($badge->name);
}
if (!empty($badge->image)) {
$imageurl = $badge->image;
}
if (isset($badge->assertion->badge->name)) {
$bname = s($badge->assertion->badge->name);
}
if (isset($badge->imageUrl)) {
$imageurl = $badge->imageUrl;
}
}
$name = html_writer::tag('span', $bname, array('class' => 'badge-name'));
@@ -58,13 +70,27 @@ class core_badges_renderer extends plugin_renderer_base {
$download = $status = $push = '';
if (($userid == $USER->id) && !$profile) {
$url = new moodle_url('mybadges.php', array('download' => $badge->id, 'hash' => $badge->uniquehash, 'sesskey' => sesskey()));
$params = array(
'download' => $badge->id,
'hash' => $badge->uniquehash,
'sesskey' => sesskey()
);
$url = new moodle_url(
'mybadges.php',
$params
);
$notexpiredbadge = (empty($badge->dateexpire) || $badge->dateexpire > time());
$backpackexists = badges_user_has_backpack($USER->id);
if (!empty($CFG->badges_allowexternalbackpack) && $notexpiredbadge && $backpackexists) {
$assertion = new moodle_url('/badges/assertion.php', array('b' => $badge->uniquehash));
$action = new component_action('click', 'addtobackpack', array('assertion' => $assertion->out(false)));
$push = $this->output->action_icon(new moodle_url('#'), new pix_icon('t/backpack', get_string('addtobackpack', 'badges')), $action);
$action = null;
if (badges_open_badges_backpack_api() == OPEN_BADGES_V1) {
$action = new component_action('click', 'addtobackpack', array('assertion' => $assertion->out(false)));
$url = new moodle_url('#');
} else {
$url = new moodle_url('/badges/backpack-add.php', array('hash' => $badge->uniquehash));
}
$push = $this->output->action_icon($url, new pix_icon('t/backpack', get_string('addtobackpack', 'badges')), $action);
}
$download = $this->output->action_icon($url, new pix_icon('t/download', get_string('download')));
@@ -260,7 +286,7 @@ class core_badges_renderer extends plugin_renderer_base {
// Edit badge.
if (has_capability('moodle/badges:configuredetails', $context)) {
$url = new moodle_url('/badges/edit.php', array('id' => $badge->id, 'action' => 'details'));
$url = new moodle_url('/badges/edit.php', array('id' => $badge->id, 'action' => 'badge'));
$actions .= $this->output->action_icon($url, new pix_icon('t/edit', get_string('edit'))) . " ";
}
@@ -281,7 +307,7 @@ class core_badges_renderer extends plugin_renderer_base {
}
// Outputs issued badge with actions available.
protected function render_issued_badge(issued_badge $ibadge) {
protected function render_issued_badge(\core_badges\output\issued_badge $ibadge) {
global $USER, $CFG, $DB, $SITE;
$issued = $ibadge->issued;
$userinfo = $ibadge->recipient;
@@ -309,15 +335,24 @@ class core_badges_renderer extends plugin_renderer_base {
get_string('download'),
'POST');
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',
'id' => 'addbutton',
'value' => get_string('addtobackpack', 'badges'));
$tobackpack = html_writer::tag('input', '', $attributes);
$this->output->add_action_handler($action, 'addbutton');
$output .= $tobackpack;
if (badges_open_badges_backpack_api() == OPEN_BADGES_V1) {
$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',
'class' => 'btn btn-secondary m-1',
'id' => 'addbutton',
'value' => get_string('addtobackpack', 'badges'));
$tobackpack = html_writer::tag('input', '', $attributes);
$this->output->add_action_handler($action, 'addbutton');
$output .= $tobackpack;
} else {
$assertion = new moodle_url('/badges/backpack-add.php', array('hash' => $issued['uid']));
$attributes = ['class' => 'btn btn-secondary m-1', 'role' => 'button'];
$tobackpack = html_writer::link($assertion, get_string('addtobackpack', 'badges'), $attributes);
$output .= $tobackpack;
}
}
}
$output .= html_writer::end_tag('div');
@@ -446,7 +481,7 @@ class core_badges_renderer extends plugin_renderer_base {
}
// Outputs external badge.
protected function render_external_badge(external_badge $ibadge) {
protected function render_external_badge(\core_badges\output\external_badge $ibadge) {
$issued = $ibadge->issued;
$assertion = $issued->assertion;
$issuer = $assertion->badge->issuer;
@@ -457,7 +492,10 @@ class core_badges_renderer extends plugin_renderer_base {
$output = '';
$output .= html_writer::start_tag('div', array('id' => 'badge'));
$output .= html_writer::start_tag('div', array('id' => 'badge-image'));
$output .= html_writer::empty_tag('img', array('src' => $issued->imageUrl));
if (isset($issued->imageUrl)) {
$issued->image = $issued->imageUrl;
}
$output .= html_writer::empty_tag('img', array('src' => $issued->image));
if (isset($assertion->expires)) {
$expiration = !strtotime($assertion->expires) ? s($assertion->expires) : strtotime($assertion->expires);
if ($expiration < $today) {
@@ -491,7 +529,9 @@ class core_badges_renderer extends plugin_renderer_base {
$output .= $this->output->heading(get_string('issuerdetails', 'badges'), 3);
$dl = array();
$dl[get_string('issuername', 'badges')] = s($issuer->name);
$dl[get_string('issuerurl', 'badges')] = html_writer::tag('a', $issuer->origin, array('href' => $issuer->origin));
if (isset($issuer->origin)) {
$dl[get_string('issuerurl', 'badges')] = html_writer::tag('a', $issuer->origin, array('href' => $issuer->origin));
}
if (isset($issuer->contact)) {
$dl[get_string('contact', 'badges')] = obfuscate_mailto($issuer->contact);
@@ -502,10 +542,15 @@ class core_badges_renderer extends plugin_renderer_base {
$dl = array();
$dl[get_string('name')] = s($assertion->badge->name);
$dl[get_string('description', 'badges')] = s($assertion->badge->description);
$dl[get_string('bcriteria', 'badges')] = html_writer::tag('a', s($assertion->badge->criteria), array('href' => $assertion->badge->criteria));
if (isset($assertion->badge->criteria)) {
$dl[get_string('bcriteria', 'badges')] = html_writer::tag(
'a',
s($assertion->badge->criteria),
array('href' => $assertion->badge->criteria)
);
}
$output .= $this->definition_list($dl);
$output .= $this->output->heading(get_string('issuancedetails', 'badges'), 3);
$dl = array();
if (isset($assertion->issued_on)) {
$issuedate = !strtotime($assertion->issued_on) ? s($assertion->issued_on) : strtotime($assertion->issued_on);
@@ -519,17 +564,24 @@ class core_badges_renderer extends plugin_renderer_base {
}
}
if (isset($assertion->evidence)) {
$dl[get_string('evidence', 'badges')] = html_writer::tag('a', s($assertion->evidence), array('href' => $assertion->evidence));
$dl[get_string('evidence', 'badges')] = html_writer::tag(
'a',
s($assertion->evidence),
array('href' => $assertion->evidence)
);
}
if (!empty($dl)) {
$output .= $this->output->heading(get_string('issuancedetails', 'badges'), 3);
$output .= $this->definition_list($dl);
}
$output .= $this->definition_list($dl);
$output .= html_writer::end_tag('div');
return $output;
}
// Displays the user badges.
protected function render_badge_user_collection(badge_user_collection $badges) {
global $CFG, $USER, $SITE;
protected function render_badge_user_collection(\core_badges\output\badge_user_collection $badges) {
global $CFG, $USER, $SITE, $OUTPUT;
$backpack = $badges->backpack;
$mybackpack = new moodle_url('/badges/mybackpack.php');
@@ -545,20 +597,23 @@ class core_badges_renderer extends plugin_renderer_base {
$searchform = $this->output->box($this->helper_search_form($badges->search), 'boxwidthwide boxaligncenter');
// Download all button.
$downloadall = $this->output->single_button(
$actionhtml = $this->output->single_button(
new moodle_url('/badges/mybadges.php', array('downloadall' => true, 'sesskey' => sesskey())),
get_string('downloadall'), 'POST', array('class' => 'activatebadge'));
$downloadall = $this->output->box('', 'col-md-3');
$downloadall .= $this->output->box($actionhtml, 'col-md-9');
$downloadall = $this->output->box($downloadall, 'row m-l-2');
// Local badges.
$localhtml = html_writer::start_tag('div', array('id' => 'issued-badge-table', 'class' => 'generalbox'));
$heading = get_string('localbadges', 'badges', format_string($SITE->fullname, true, array('context' => context_system::instance())));
$sitename = format_string($SITE->fullname, true, array('context' => context_system::instance()));
$heading = get_string('localbadges', 'badges', $sitename);
$localhtml .= $this->output->heading_with_help($heading, 'localbadgesh', 'badges');
if ($badges->badges) {
$downloadbutton = $this->output->heading(get_string('badgesearned', 'badges', $badges->totalcount), 4, 'activatebadge');
$downloadbutton .= $downloadall;
$countmessage = $this->output->box(get_string('badgesearned', 'badges', $badges->totalcount));
$htmllist = $this->print_badges_list($badges->badges, $USER->id);
$localhtml .= $backpackconnect . $downloadbutton . $searchform . $htmlpagingbar . $htmllist . $htmlpagingbar;
$localhtml .= $backpackconnect . $countmessage . $searchform . $htmlpagingbar . $htmllist . $htmlpagingbar . $downloadall;
} else {
$localhtml .= $searchform . $this->output->notification(get_string('nobadges', 'badges'));
}
@@ -570,13 +625,17 @@ class core_badges_renderer extends plugin_renderer_base {
$externalhtml .= html_writer::start_tag('div', array('class' => 'generalbox'));
$externalhtml .= $this->output->heading_with_help(get_string('externalbadges', 'badges'), 'externalbadges', 'badges');
if (!is_null($backpack)) {
if ($backpack->backpackid != $CFG->badges_site_backpack) {
$externalhtml .= $OUTPUT->notification(get_string('backpackneedsupdate', 'badges'), 'warning');
}
if ($backpack->totalcollections == 0) {
$externalhtml .= get_string('nobackpackcollections', 'badges', $backpack);
$externalhtml .= get_string('nobackpackcollectionssummary', 'badges', $backpack);
} else {
if ($backpack->totalbadges == 0) {
$externalhtml .= get_string('nobackpackbadges', 'badges', $backpack);
$externalhtml .= get_string('nobackpackbadgessummary', 'badges', $backpack);
} else {
$externalhtml .= get_string('backpackbadges', 'badges', $backpack);
$externalhtml .= get_string('backpackbadgessummary', 'badges', $backpack);
$externalhtml .= '<br/><br/>' . $this->print_badges_list($backpack->badges, $USER->id, true, true);
}
}
@@ -585,13 +644,20 @@ class core_badges_renderer extends plugin_renderer_base {
}
$externalhtml .= html_writer::end_tag('div');
$attr = ['class' => 'btn btn-secondary'];
$label = get_string('backpackbadgessettings', 'badges');
$backpacksettings = html_writer::link(new moodle_url('/badges/mybackpack.php'), $label, $attr);
$actionshtml = $this->output->box('', 'col-md-3');
$actionshtml .= $this->output->box($backpacksettings, 'col-md-9');
$actionshtml = $this->output->box($actionshtml, 'row m-l-2');
$externalhtml .= $actionshtml;
}
return $localhtml . $externalhtml;
}
// Displays the available badges.
protected function render_badge_collection(badge_collection $badges) {
protected function render_badge_collection(\core_badges\output\badge_collection $badges) {
$paging = new paging_bar($badges->totalcount, $badges->page, $badges->perpage, $this->page->url, 'page');
$htmlpagingbar = $this->render($paging);
$table = new html_table();
@@ -634,7 +700,7 @@ class core_badges_renderer extends plugin_renderer_base {
}
// Outputs table of badges with actions available.
protected function render_badge_management(badge_management $badges) {
protected function render_badge_management(\core_badges\output\badge_management $badges) {
$paging = new paging_bar($badges->totalcount, $badges->page, $badges->perpage, $this->page->url, 'page');
// New badge button.
@@ -642,7 +708,8 @@ class core_badges_renderer extends plugin_renderer_base {
if (has_capability('moodle/badges:createbadge', $this->page->context)) {
$n['type'] = $this->page->url->get_param('type');
$n['id'] = $this->page->url->get_param('id');
$htmlnew = $this->output->single_button(new moodle_url('newbadge.php', $n), get_string('newbadge', 'badges'));
$btn = $this->output->single_button(new moodle_url('newbadge.php', $n), get_string('newbadge', 'badges'));
$htmlnew = $this->output->box($btn);
}
$htmlpagingbar = $this->render($paging);
@@ -700,7 +767,7 @@ class core_badges_renderer extends plugin_renderer_base {
if (has_capability('moodle/badges:configuredetails', $context)) {
$row[] = new tabobject('details',
new moodle_url('/badges/edit.php', array('id' => $badgeid, 'action' => 'details')),
new moodle_url('/badges/edit.php', array('id' => $badgeid, 'action' => 'badge')),
get_string('bdetails', 'badges')
);
}
@@ -912,7 +979,7 @@ class core_badges_renderer extends plugin_renderer_base {
// Renders a table with users who have earned the badge.
// Based on stamps collection plugin.
protected function render_badge_recipients(badge_recipients $recipients) {
protected function render_badge_recipients(\core_badges\output\badge_recipients $recipients) {
$paging = new paging_bar($recipients->totalcount, $recipients->page, $recipients->perpage, $this->page->url, 'page');
$htmlpagingbar = $this->render($paging);
$table = new html_table();
@@ -1128,10 +1195,10 @@ class core_badges_renderer extends plugin_renderer_base {
/**
* Renders a table for related badges.
*
* @param badge_related $related list related badges.
* @param \core_badges\output\badge_related $related list related badges.
* @return string list related badges to output.
*/
protected function render_badge_related(badge_related $related) {
protected function render_badge_related(\core_badges\output\badge_related $related) {
$currentbadge = new badge($related->currentbadgeid);
$languages = get_string_manager()->get_list_of_languages();
$paging = new paging_bar($related->totalcount, $related->page, $related->perpage, $this->page->url, 'page');
@@ -1188,10 +1255,10 @@ class core_badges_renderer extends plugin_renderer_base {
/**
* Renders a table with alignment.
*
* @param badge_alignments $alignments List alignments.
* @param core_badges\output\badge_alignments $alignments List alignments.
* @return string List alignment to output.
*/
protected function render_badge_alignments(badge_alignments $alignments) {
protected function render_badge_alignments(\core_badges\output\badge_alignments $alignments) {
$currentbadge = new badge($alignments->currentbadgeid);
$paging = new paging_bar($alignments->totalcount, $alignments->page, $alignments->perpage, $this->page->url, 'page');
$htmlpagingbar = $this->render($paging);
@@ -1236,287 +1303,16 @@ class core_badges_renderer extends plugin_renderer_base {
return $htmlpagingbar . $htmltable . $htmlpagingbar;
}
}
/**
* An issued badges for badge.php page
*/
class issued_badge implements renderable {
/** @var issued badge */
public $issued;
/** @var badge recipient */
public $recipient;
/** @var badge class */
public $badgeclass;
/** @var badge visibility to others */
public $visible = 0;
/** @var badge class */
public $badgeid = 0;
/**
* Initializes the badge to display
* Defer to template.
*
* @param string $hash Issued badge hash
* @param \core_badges\output\external_backpacks_page $page
* @return bool|string
*/
public function __construct($hash) {
global $DB;
$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}
WHERE ' . $DB->sql_compare_text('uniquehash', 40) . ' = ' . $DB->sql_compare_text(':hash', 40),
array('hash' => $hash), IGNORE_MISSING);
if ($rec) {
// Get a recipient from database.
$namefields = get_all_user_name_fields(true, 'u');
$user = $DB->get_record_sql("SELECT u.id, $namefields, u.deleted, u.email
FROM {user} u WHERE u.id = :userid", array('userid' => $rec->userid));
$this->recipient = $user;
$this->visible = $rec->visible;
$this->badgeid = $rec->badgeid;
}
public function render_external_backpacks_page(\core_badges\output\external_backpacks_page $page) {
$data = $page->export_for_template($this);
return parent::render_from_template('core_badges/external_backpacks_page', $data);
}
}
/**
* An external badges for external.php page
*/
class external_badge implements renderable {
/** @var issued badge */
public $issued;
/** @var User ID */
public $recipient;
/** @var validation of external badge */
public $valid = true;
/**
* Initializes the badge to display
*
* @param object $badge External badge information.
* @param int $recipient User id.
*/
public function __construct($badge, $recipient) {
global $DB;
// At this point a user has connected a backpack. So, we are going to get
// their backpack email rather than their account email.
$namefields = get_all_user_name_fields(true, 'u');
$user = $DB->get_record_sql("SELECT {$namefields}, b.email
FROM {user} u INNER JOIN {badge_backpack} b ON u.id = b.userid
WHERE userid = :userid", array('userid' => $recipient), IGNORE_MISSING);
$this->issued = $badge;
$this->recipient = $user;
// Check if recipient is valid.
// There is no way to be 100% sure that a badge belongs to a user.
// Backpack does not return any recipient information.
// All we can do is compare that backpack email hashed using salt
// provided in the assertion matches a badge recipient from the assertion.
if ($user) {
if (validate_email($badge->assertion->recipient) && $badge->assertion->recipient == $user->email) {
// If we have email, compare emails.
$this->valid = true;
} else if ($badge->assertion->recipient == 'sha256$' . hash('sha256', $user->email)) {
// If recipient is hashed, but no salt, compare hashes without salt.
$this->valid = true;
} else if ($badge->assertion->recipient == 'sha256$' . hash('sha256', $user->email . $badge->assertion->salt)) {
// If recipient is hashed, compare hashes.
$this->valid = true;
} else {
// Otherwise, we cannot be sure that this user is a recipient.
$this->valid = false;
}
} else {
$this->valid = false;
}
}
}
/**
* Badge recipients rendering class
*/
class badge_recipients implements renderable {
/** @var string how are the data sorted */
public $sort = 'lastname';
/** @var string how are the data sorted */
public $dir = 'ASC';
/** @var int page number to display */
public $page = 0;
/** @var int number of badge recipients to display per page */
public $perpage = 30;
/** @var int the total number or badge recipients to display */
public $totalcount = null;
/** @var array internal list of badge recipients ids */
public $userids = array();
/**
* Initializes the list of users to display
*
* @param array $holders List of badge holders
*/
public function __construct($holders) {
$this->userids = $holders;
}
}
/**
* Collection of all badges for view.php page
*/
class badge_collection implements renderable {
/** @var string how are the data sorted */
public $sort = 'name';
/** @var string how are the data sorted */
public $dir = 'ASC';
/** @var int page number to display */
public $page = 0;
/** @var int number of badges to display per page */
public $perpage = BADGE_PERPAGE;
/** @var int the total number of badges to display */
public $totalcount = null;
/** @var array list of badges */
public $badges = array();
/**
* Initializes the list of badges to display
*
* @param array $badges Badges to render
*/
public function __construct($badges) {
$this->badges = $badges;
}
}
/**
* Collection of badges used at the index.php page
*/
class badge_management extends badge_collection implements renderable {
}
/**
* Collection of user badges used at the mybadges.php page
*/
class badge_user_collection extends badge_collection implements renderable {
/** @var array backpack settings */
public $backpack = null;
/** @var string search */
public $search = '';
/**
* Initializes user badge collection.
*
* @param array $badges Badges to render
* @param int $userid Badges owner
*/
public function __construct($badges, $userid) {
global $CFG;
parent::__construct($badges);
if (!empty($CFG->badges_allowexternalbackpack)) {
$this->backpack = get_backpack_settings($userid, true);
}
}
}
/**
* Collection of all related badges.
*
* @copyright 2018 Tung Thai
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Tung Thai <[email protected]>
*/
class badge_related implements renderable {
/** @var string how are the data sorted. */
public $sort = 'name';
/** @var string how are the data sorted. */
public $dir = 'ASC';
/** @var int page number to display. */
public $page = 0;
/** @var int number of badges to display per page. */
public $perpage = BADGE_PERPAGE;
/** @var int the total number of badges to display. */
public $totalcount = null;
/** @var int the current badge. */
public $currentbadgeid = 0;
/** @var array list of badges. */
public $badges = array();
/**
* Initializes the list of badges to display.
*
* @param array $badges related badges to render.
* @param int $currentbadgeid ID current badge.
*/
public function __construct($badges, $currentbadgeid) {
$this->badges = $badges;
$this->currentbadgeid = $currentbadgeid;
}
}
/**
* Collection of all alignments.
*
* @copyright 2018 Tung Thai
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Tung Thai <[email protected]>
*/
class badge_alignments implements renderable
{
/** @var string how are the data sorted. */
public $sort = 'name';
/** @var string how are the data sorted. */
public $dir = 'ASC';
/** @var int page number to display. */
public $page = 0;
/** @var int number of badges to display per page. */
public $perpage = BADGE_PERPAGE;
/** @var int the total number of badges to display. */
public $totalcount = null;
/** @var array list of badges. */
public $alignments = array();
/** @var array list of badges. */
public $currentbadgeid = 0;
/**
* Initializes the list of alignments to display.
*
* @param array $alignments List alignments to render.
* @param int $currentbadgeid ID current badge.
*/
public function __construct($alignments, $currentbadgeid) {
$this->alignments = $alignments;
$this->currentbadgeid = $currentbadgeid;
}
}
@@ -0,0 +1,48 @@
{{!
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/>.
}}
{{!
Manage backpacks.
Context variables required for this template:
* backpacks - array of objects containing id, backpackurl, enabled, apiversion and sortorder
}}
<table class="generaltable fullwidth">
<caption>{{#str}}listbackpacks, core_badges{{/str}}</caption>
<thead>
<tr>
<th scope="col">{{#str}}backpackweburl, core_badges{{/str}}</th>
<th scope="col">{{#str}}sitebackpack, core_badges{{/str}}</th>
<th scope="col">{{#str}}actions, core_badges{{/str}}</th>
</tr>
</thead>
<tbody>
{{#backpacks}}
<tr>
<td> {{{backpackweburl}}} </td>
<td> {{#sitebackpack}}Yes{{/sitebackpack}} </td>
<td>
{{#canedit}}
<a href="{{baseurl}}?id={{id}}&action=edit">
{{#str}}editsettings, core_badges{{/str}}
</a>
{{/canedit}}
</td>
</td>
</tr>
{{/backpacks}}
</tbody>
</table>
+5 -4
View File
@@ -136,15 +136,16 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
// Format JSON-LD for Openbadge specification version 2.0.
$this->assertion2 = new stdClass();
$this->assertion2->badge = '{"recipient":{"identity":"%s","type":"email","hashed":true,"salt":"%s"},' .
'"badge":{"name":"%s","description":"%s","image":{"id":"%s","author":"%s","caption":"%s"},' .
'"badge":{"name":"%s","description":"%s","image":"%s",' .
'"criteria":{"id":"%s","narrative":"%s"},"issuer":{"name":"%s","url":"%s","email":"%s",' .
'"@context":"https:\/\/w3id.org\/openbadges\/v2","id":"%s","type":"Issuer"},' .
'"@context":"https:\/\/w3id.org\/openbadges\/v2","id":"%s","type":"BadgeClass","version":"%s",' .
'"@language":"%s","related":[{"id":"%s","version":"%s","@language":"%s"}],"endorsement":"%s",' .
'"alignment":[{"targetName":"%s","targetUrl":"%s","targetDescription":"%s","targetFramework":"%s",' .
'"@language":"en","related":[{"id":"%s","version":"%s","@language":"%s"}],"endorsement":"%s",' .
'"alignments":[{"targetName":"%s","targetUrl":"%s","targetDescription":"%s","targetFramework":"%s",' .
'"targetCode":"%s"}]},"verify":{"type":"hosted","url":"%s"},"issuedOn":"%s","evidence":"%s",' .
'"@context":"https:\/\/w3id.org\/openbadges\/v2","type":"Assertion","id":"%s"}';
$this->assertion2->class = '{"name":"%s","description":"%s","image":{"id":"%s","author":"%s","caption":"%s"},' .
$this->assertion2->class = '{"name":"%s","description":"%s","image":"%s",' .
'"criteria":{"id":"%s","narrative":"%s"},"issuer":{"name":"%s","url":"%s","email":"%s",' .
'"@context":"https:\/\/w3id.org\/openbadges\/v2","id":"%s","type":"Issuer"},' .
'"@context":"https:\/\/w3id.org\/openbadges\/v2","id":"%s","type":"BadgeClass","version":"%s",' .
+2
View File
@@ -177,6 +177,8 @@ class core_badges_external_testcase extends externallib_advanced_testcase {
if (!empty($alignments)) {
foreach ($alignments as $alignment) {
// Students cannot see some fields of the alignments.
unset($alignment->targetname);
unset($alignment->targeturl);
unset($alignment->targetdescription);
unset($alignment->targetframework);
unset($alignment->targetcode);
+1 -1
View File
@@ -95,7 +95,7 @@ if ($totalcount) {
echo $OUTPUT->box(get_string('error:notifycoursedate', 'badges'), 'generalbox notifyproblem');
}
$badges = new badge_collection($records);
$badges = new \core_badges\output\badge_collection($records);
$badges->sort = $sortby;
$badges->dir = $sorthow;
$badges->page = $page;
+35 -12
View File
@@ -34,6 +34,7 @@ $string['addcompetency'] = 'Add competency';
$string['addcompetency_help'] = 'Select all competencies that should be added to this badge requirement. Hold CTRL key to select multiple items.';
$string['addbadgecriteria'] = 'Add badge criteria';
$string['addcriteria'] = 'Add criteria';
$string['addbackpack'] = 'Add backpack';
$string['addcriteriatext'] = 'To start adding criteria, please select one of the options from the drop-down menu.';
$string['addcohort'] = 'Add cohort';
$string['addcohort_help'] = 'Select all cohorts that should be added to this badge requirement. Hold CTRL key to select multiple items.';
@@ -41,6 +42,7 @@ $string['addcourse'] = 'Add courses';
$string['addcourse_help'] = 'Select all courses that should be added to this badge requirement. Hold CTRL key to select multiple items.';
$string['addrelated'] = 'Add related badge';
$string['addtobackpack'] = 'Add to backpack';
$string['addedtobackpack'] = 'Added badge to backpack';
$string['adminonly'] = 'This page is restricted to site administrators only.';
$string['after'] = 'after the date of issue.';
$string['aggregationmethod'] = 'Aggregation method';
@@ -69,6 +71,7 @@ $string['anymethodcompetencies'] = 'Any of the selected competencies have been c
$string['anymethodcourseset'] = 'Any of the selected courses is complete';
$string['anymethodmanual'] = 'Any of the selected roles awards the badge';
$string['anymethodprofile'] = 'Any of the selected profile fields has been completed';
$string['apiversion'] = 'API version supported';
$string['archivebadge'] = 'Would you like to delete badge \'{$a}\', but keep existing issued badges?';
$string['archiveconfirm'] = 'Delete and keep existing issued badges';
$string['archivehelp'] = '<p>This option means that the badge will be marked as "retired" and will no longer appear in the list of badges. Users will no longer be able to earn this badge, however existing badge recipients will still be able to display this badge on their profile page and push it to their external backpacks.</p>
@@ -80,6 +83,7 @@ $string['awardedtoyou'] = 'Issued to me';
$string['awardoncron'] = 'Access to the badges was successfully enabled. Too many users can instantly earn this badge. To ensure site performance, this action will take some time to process.';
$string['awards'] = 'Recipients';
$string['backpackavailability'] = 'External badge verification';
$string['backpackneedsupdate'] = 'The backpack connected to this profile does not match the backpack for the site. You need to disconnect and reconnect the backpack.';
$string['backpackavailability_help'] = 'For badge recipients to be able to prove they earned their badges from you, an external backpack service should be able to access your site and verify badges issued from it. Your site does not currently appear to be accessible, which means that badges you have already issued or will issue in the future cannot be verified.
**Why am I seeing this message?**
@@ -93,12 +97,11 @@ You should fix this issue on any production site where you are planning to issue
**What if I can\'t make my whole site publicly accessible?**
The only URL required for verification is [your-site-url]/badges/assertion.php so if you are able to modify your firewall to allow external access to that file, badge verification will still work.';
$string['backpackbadges'] = 'You have {$a->totalbadges} badge(s) displayed from {$a->totalcollections} collection(s). <a href="mybackpack.php">Change backpack settings</a>.';
$string['backpackbadgessummary'] = 'You have {$a->totalbadges} badge(s) displayed from {$a->totalcollections} collection(s).';
$string['backpackbadgessettings'] = 'Change backpack settings';
$string['backpackcannotsendverification'] = 'Cannot send verification email';
$string['backpackconnection'] = 'Backpack connection';
$string['backpackconnection_help'] = 'This page allows you to set up connection to an external backpack provider. Connecting to a backpack lets you display external badges within this site and push badges earned here to your backpack.
Currently, only <a href="http://backpack.openbadges.org">Mozilla OpenBadges Backpack</a> is supported. You need to sign up for a backpack service before trying to set up backpack connection on this page.';
$string['backpackconnection_help'] = 'This page allows you to set up connection to an external backpack provider. Connecting to a backpack lets you display external badges within this site and push badges earned here to your backpack.';
$string['backpackconnectioncancelattempt'] = 'Connect using a different email address';
$string['backpackconnectionconnect'] = 'Connect to backpack';
$string['backpackconnectionresendemail'] = 'Resend verification email';
@@ -127,6 +130,9 @@ $string['backpackimport'] = 'Badge import settings';
$string['backpackimport_help'] = 'After the backpack connection is successfully established, badges from your backpack can be displayed on your badges page and your profile page.
In this area, you can select collections of badges from your backpack that you would like to display in your profile.';
$string['backpacksettings'] = 'Backpack settings';
$string['backpackapiurl'] = 'Backpack API URL';
$string['backpackweburl'] = 'Backpack Website URL';
$string['badges'] = 'Badges';
$string['badgedetails'] = 'Badge details';
$string['badgeimage'] = 'Image';
@@ -255,11 +261,14 @@ $string['dateearned'] = 'Date: {$a}';
$string['day'] = 'Day(s)';
$string['deactivate'] = 'Disable access';
$string['deactivatesuccess'] = 'Access to the badges was successfully disabled.';
$string['defaultissuercontact'] = 'Default badge issuer contact details';
$string['defaultissuercontact_desc'] = 'An email address associated with the badge issuer.';
$string['defaultissuername'] = 'Default badge issuer name';
$string['defaultissuercontact'] = 'Badge issuer email address';
$string['defaultissuercontact_desc'] = 'An email address associated with the badge issuer. For an Open Badges v2.0 backpack, this is used for authentication when publishing badges to a backpack.';
$string['defaultissuerpassword'] = 'Badge issuer password';
$string['defaultissuerpassword_help'] = 'When publishing Badges to a Backpack using Open Badges v2.0, the "Badge issuer email address" and "Badge issuer password" must correspond to an account on the Backpack site that is used to issue Badges representing this site.';
$string['defaultissuername'] = 'Badge issuer name';
$string['defaultissuername_desc'] = 'Name of the issuing agent or authority.';
$string['delbadge'] = 'Would you like to delete badge \'{$a}\' and remove all existing issued badges?';
$string['delexternalbackpackconfirm'] = 'Delete site backpack \'{$a}\'?';
$string['delconfirm'] = 'Delete and remove existing issued badges';
$string['deletehelp'] = '<p>Fully deleting a badge means that all its information and criteria records will be permanently removed. Users who have earned this badge will no longer be able to access it and display it on their profile pages.</p>
<p>Note: Users who have earned this badge and have already pushed it to their external backpack, will still have this badge in their external backpack. However, they will not be able to access criteria and evidence pages linking back to this web site.</p>';
@@ -296,8 +305,8 @@ $string['error:invalidparamcohort'] = 'Cohort does not exist. ';
$string['error:noactivities'] = 'There are no activities with completion criteria enabled in this course.';
$string['error:nocohorts'] = 'No cohorts';
$string['error:nocourses'] = 'Course completion is not enabled for any of the courses in this site, so none can be displayed. Course completion may be enabled in the course settings.';
$string['error:nogroups'] = '<p>There are no public collections of badges available in your backpack. </p>
<p>Only public collections are shown, <a href="http://backpack.openbadges.org">visit your backpack</a> to create some public collections.</p>';
$string['error:nogroupssummary'] = '<p>There are no public collections of badges available in your backpack. </p>';
$string['error:nogroupslink'] = '<p>Only public collections are shown, <a href="{$a}" target="_blank" rel="nofollow">visit your backpack</a> to create some public collections.</p>';
$string['error:nopermissiontoview'] = 'You have no permissions to view badge recipients';
$string['error:nosuchbadge'] = 'Badge with id {$a} does not exist.';
$string['error:nosuchcohort'] = 'Warning: This cohort is no longer available.';
@@ -338,11 +347,13 @@ $string['expireperiodm'] = 'This badge expires {$a} minute(s) after being issued
$string['expireperiods'] = 'This badge expires {$a} second(s) after being issued.';
$string['expirydate'] = 'Expiry date';
$string['expirydate_help'] = 'Optionally, badges can expire on a specific date, or the date can be calculated based on the date when the badge was issued to a user. ';
$string['existsinbackpack'] = 'Badge already exists in backpack';
$string['externalconnectto'] = 'To display external badges you need to <a href="{$a}">connect to a backpack</a>.';
$string['externalbadges'] = 'My badges from other web sites';
$string['externalbadgesp'] = 'Badges from other web sites:';
$string['externalbadges_help'] = 'This area displays badges from your external backpack.';
$string['fixed'] = 'Fixed date';
$string['hidden'] = 'Hidden';
$string['hiddenbadge'] = 'Unfortunately, the badge owner has not made this information available.';
$string['imageauthoremail'] = 'Image author\'s email';
$string['imageauthoremail_help'] = 'If specified, the email address of the badge image author is displayed on the badge page.';
@@ -364,6 +375,7 @@ $string['issuerurl'] = 'Issuer URL';
$string['issuerurl_help'] = 'The website of the organisation issuing the endorsement. The URL should have a prefix http:// or https://.';
$string['language'] = 'Language';
$string['language_help'] = 'The language used on the badge page.';
$string['listbackpacks'] = 'List of backpacks';
$string['localconnectto'] = 'To share these badges outside this web site you need to <a href="{$a}">connect to a backpack</a>.';
$string['localbadges'] = 'My badges from {$a} web site';
$string['localbadgesh'] = 'My badges from this web site';
@@ -376,6 +388,7 @@ $string['localbadgesp'] = 'Badges from {$a}:';
$string['makeprivate'] = 'Make private';
$string['makepublic'] = 'Make public';
$string['managebadges'] = 'Manage badges';
$string['managebackpacks'] = 'Manage backpacks';
$string['message'] = 'Message body';
$string['messagebody'] = '<p>You have been awarded the badge "%badgename%"!</p>
<p>More information about this badge can be found on the %badgelink% badge information page.</p>
@@ -392,8 +405,9 @@ $string['newimage'] = 'New image';
$string['noalignment'] = 'This badge does not have any external skills or standards specified.';
$string['noawards'] = 'This badge has not been earned yet.';
$string['nobackpack'] = 'There is no backpack service connected to this account.<br/>';
$string['nobackpackbadges'] = 'There are no badges in the collections you have selected. <a href="mybackpack.php">Add more collections</a>.';
$string['nobackpackcollections'] = 'No badge collections have been selected. <a href="mybackpack.php">Add collections</a>.';
$string['nobackpackbadgessummary'] = 'There are no badges in the collections you have selected.';
$string['nobackpackcollectionssummary'] = 'No badge collections have been selected.';
$string['nobackpacks'] = 'There are no backpacks available';
$string['nobadges'] = 'There are no badges available.';
$string['nocompetencies'] = 'No competencies selected.';
$string['nocriteria'] = 'Criteria for this badge have not been set up yet.';
@@ -429,6 +443,8 @@ $string['notifyweekly'] = 'Weekly';
$string['numawards'] = 'This badge has been issued to <a href="{$a->link}">{$a->count}</a> user(s).';
$string['numawardstat'] = 'This badge has been issued {$a} user(s).';
$string['overallcrit'] = 'of the selected criteria are complete.';
$string['openbadgesv1'] = 'Open Badges v1.0';
$string['openbadgesv2'] = 'Open Badges v2.0';
$string['potentialrecipients'] = 'Potential badge recipients';
$string['preferences'] = 'Badge preferences';
$string['privacy:metadata:backpack'] = 'A record of user\'s backpacks';
@@ -481,10 +497,12 @@ $string['reviewconfirm'] = '<p>This will make your badge visible to users and al
$string['save'] = 'Save';
$string['searchname'] = 'Search by name';
$string['selectaward'] = 'Please select the role you would like to use to award this badge: ';
$string['selectgroup_end'] = 'Only public collections are shown, <a href="http://backpack.openbadges.org">visit your backpack</a> to create more public collections.';
$string['selectgroup_end'] = 'Only public collections are shown, <a href="{$a}">visit your backpack</a> to create more public collections.';
$string['selectgroup_start'] = 'Select collections from your backpack to display on this site:';
$string['selecting'] = 'With selected badges...';
$string['setup'] = 'Set up connection';
$string['sitebackpack'] = 'Site backpack';
$string['sitebackpack_help'] = 'The external backpack to allow users to share their badges with. Changing this setting after users have connected their backpacks will require each user to update their backpack settings.';
$string['sitebadges'] = 'Site badges';
$string['sitebadges_help'] = 'Site badges can only be awarded to users for site-related activities. These include completing a set of courses or parts of user profiles. Site badges can also be issued manually by one user to another.
@@ -549,3 +567,8 @@ $string['signinwithyouremail'] = 'Sign in with your email';
// Deprecated since Moodle 3.6.
$string['error:invalidbadgeurl'] = 'Invalid issuer URL format. The URL should have a prefix http:// or https://.';
$string['backpackbadges'] = 'You have {$a->totalbadges} badge(s) displayed from {$a->totalcollections} collection(s). <a href="mybackpack.php">Change backpack settings</a>.';
$string['error:nogroups'] = '<p>There are no public collections of badges available in your backpack. </p>
<p>Only public collections are shown, <a href="http://backpack.openbadges.org">visit your backpack</a> to create some public collections.</p>';
$string['nobackpackbadges'] = 'There are no badges in the collections you have selected. <a href="mybackpack.php">Add more collections</a>.';
$string['nobackpackcollections'] = 'No badge collections have been selected. <a href="mybackpack.php">Add collections</a>.';
+4
View File
@@ -154,3 +154,7 @@ undockall,core_block
undockblock,core_block
undockitem,core_block
canceledit,core_message
backpackbadges,core_badges
nobackpackbadges,core_badges
nobackpackcollections,core_badges
error:nogroups,core_badges
+366 -886
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -319,4 +319,7 @@ function xmldb_main_install() {
require_once($CFG->libdir . '/db/upgradelib.php');
make_default_scale();
make_competence_scale();
require_once($CFG->libdir . '/badgeslib.php');
badges_install_default_backpacks();
}
Regular → Executable
+32 -1
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/db" VERSION="20190412" COMMENT="XMLDB file for core Moodle tables"
<XMLDB PATH="lib/db" VERSION="20190501" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
@@ -3171,6 +3171,7 @@
<FIELD NAME="backpackuid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="autosync" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="password" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="apiversion" TYPE="char" LENGTH="12" NOTNULL="true" DEFAULT="1.0" SEQUENCE="false" COMMENT="Record the supported version of the OpenBadges API from this backpack."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
@@ -3182,12 +3183,27 @@
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="backpackid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of a backpack"/>
<FIELD NAME="collectionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Badge collection id in the backpack"/>
<FIELD NAME="entityid" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="fk_backpackid" TYPE="foreign" FIELDS="backpackid" REFTABLE="badge_backpack" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="badge_external_identifier" COMMENT="Setting for external badges mappings">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="sitebackpackid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of a site backpack"/>
<FIELD NAME="internalid" TYPE="char" LENGTH="128" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="externalid" TYPE="char" LENGTH="128" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="type" TYPE="char" LENGTH="16" NOTNULL="true" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="fk_backpackid" TYPE="foreign" FIELDS="sitebackpackid" REFTABLE="badge_backpack" REFFIELDS="id"/>
<KEY NAME="backpack-internal-external" TYPE="unique" FIELDS="sitebackpackid, internalid, externalid, type"/>
</KEYS>
</TABLE>
<TABLE NAME="badge_alignment" COMMENT="Defines alignment for badges">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
@@ -3216,6 +3232,21 @@
<KEY NAME="badgeid-relatedbadgeid" TYPE="unique" FIELDS="badgeid, relatedbadgeid"/>
</KEYS>
</TABLE>
<TABLE NAME="badge_external_backpack" COMMENT="Defines settings for site level backpacks that a user can connect to.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="backpackapiurl" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="backpackweburl" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="apiversion" TYPE="char" LENGTH="12" NOTNULL="true" DEFAULT="1.0" SEQUENCE="false"/>
<FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="password" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Password to login into external backpack and issue badges."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="backpackapiurlkey" TYPE="unique" FIELDS="backpackapiurl"/>
<KEY NAME="backpackweburlkey" TYPE="unique" FIELDS="backpackweburl"/>
</KEYS>
</TABLE>
<TABLE NAME="user_devices" COMMENT="This table stores user's mobile devices information in order to send PUSH notifications">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
Regular → Executable
+68
View File
@@ -3301,5 +3301,73 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2019042700.01);
}
if ($oldversion < 2019050100.01) {
// Define field apiversion to be added to badge_backpack.
$table = new xmldb_table('badge_backpack');
$field = new xmldb_field('apiversion', XMLDB_TYPE_CHAR, '12', null, XMLDB_NOTNULL, null, '1.0', 'password');
// Conditionally launch add field apiversion.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Define table badge_external_backpack to be created.
$table = new xmldb_table('badge_external_backpack');
// Adding fields to table badge_external_backpack.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('backpackapiurl', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
$table->add_field('backpackweburl', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
$table->add_field('apiversion', XMLDB_TYPE_CHAR, '12', null, XMLDB_NOTNULL, null, '1.0');
$table->add_field('sortorder', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
$table->add_field('password', XMLDB_TYPE_CHAR, '255', null, null, null, null);
// Adding keys to table badge_external_backpack.
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
$table->add_key('backpackapiurlkey', XMLDB_KEY_UNIQUE, ['backpackapiurl']);
$table->add_key('backpackweburlkey', XMLDB_KEY_UNIQUE, ['backpackweburl']);
// Conditionally launch create table for badge_external_backpack.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Define field entityid to be added to badge_external.
$table = new xmldb_table('badge_external');
$field = new xmldb_field('entityid', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'collectionid');
// Conditionally launch add field entityid.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Add default backpacks.
require_once($CFG->libdir.'/badgeslib.php'); // Core Upgrade-related functions for badges only.
badges_install_default_backpacks();
// Define table badge_external_identifier to be created.
$table = new xmldb_table('badge_external_identifier');
// Adding fields to table badge_external_identifier.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('sitebackpackid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('internalid', XMLDB_TYPE_CHAR, '128', null, XMLDB_NOTNULL, null, null);
$table->add_field('externalid', XMLDB_TYPE_CHAR, '128', null, XMLDB_NOTNULL, null, null);
$table->add_field('type', XMLDB_TYPE_CHAR, '16', null, XMLDB_NOTNULL, null, null);
// Adding keys to table badge_external_identifier.
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
$table->add_key('fk_backpackid', XMLDB_KEY_FOREIGN, ['sitebackpackid'], 'badge_backpack', ['id']);
$table->add_key('backpack-internal-external', XMLDB_KEY_UNIQUE, ['sitebackpackid', 'internalid', 'externalid', 'type']);
// Conditionally launch create table for badge_external_identifier.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2019050100.01);
}
return true;
}
+1 -1
View File
@@ -1407,7 +1407,7 @@ function disable_output_buffering() {
*/
function is_major_upgrade_required() {
global $CFG;
$lastmajordbchanges = 2019041000.03;
$lastmajordbchanges = 2019050100.01;
$required = empty($CFG->version);
$required = $required || (float)$CFG->version < $lastmajordbchanges;
+1 -1
View File
@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2019050100.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2019050100.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.