Skip to content

Commit

Permalink
Merge branch 'MDL-40848_master' of git://github.com/totara/openbadges
Browse files Browse the repository at this point in the history
Conflicts:
	theme/base/style/core.css
	theme/bootstrapbase/less/moodle/core.less
	theme/bootstrapbase/style/moodle.css
  • Loading branch information
Sam Hemelryk committed Sep 25, 2013
2 parents 093a6fe + f6ebcd3 commit e50df55
Show file tree
Hide file tree
Showing 9 changed files with 277 additions and 27 deletions.
85 changes: 84 additions & 1 deletion badges/backpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,87 @@ function check_site_access() {
});

return false;
}
}

/*
* Update the status indicator to show connection progress.
*/
function badges_set_connection_progress(status) {
switch (status) {
case 'connecting':
var connecting = M.util.get_string('connecting', 'badges');
var imageurl = M.util.image_url('i/loading_small', 'moodle');
var loading = Y.Node.create(connecting + '&nbsp;<img src="'+imageurl+'" width="16" height="16" alt="'+connecting+'"/>');
Y.one('#connection-status').removeClass('notconnected').addClass('connecting').setHTML(loading);
break;
case 'notconnected':
var notconnected = M.util.get_string('notconnected', 'badges');;
Y.one('#connection-status').removeClass('connecting').addClass('notconnected').setHTML(notconnected);
break;
default:
// Unknown status, do nothing.
}
}

/*
* Print an error message at the top of the page.
*/
function badges_set_error_message(messagekey, param) {
var errortext = M.util.get_string(messagekey, 'badges', param);
Y.one('#connection-error').setHTML(errortext);
}

/*
* Handle the assertion generated by the Persona dialog.
*/
function badges_handle_assertion(assertion) {

if (!assertion) {
var noassertionstr = M.util.get_string('error:noassertion', 'badges');
badges_set_error_message('error:backpackloginfailed', noassertionstr);
return;
}

badges_set_connection_progress('connecting');

Y.io("backpackconnect.php", {
method: "POST",
data: "assertion="+assertion+"&sesskey="+M.cfg.sesskey,
on: {
success: function (id, result) {
// Reload page to display connected email address.
window.location.href = "mybackpack.php";
},
failure: function (id, result) {
try {
var parsedResponse = Y.JSON.parse(result.response);
} catch (e) {
badges_set_connection_progress('notconnected');
var badjsonstr = M.util.get_string('error:badjson', 'badges');
badges_set_error_message('error:backpackloginfailed', badjsonstr);
return;
}
badges_set_connection_progress('notconnected');
badges_set_error_message('error:backpackloginfailed', parsedResponse.reason);
return;
}
}
});
}

/**
* Create and bind the persona login button.
*/
function badges_init_persona_login_button() {
// Create the login button and add to the page via Javascript.
var imageurl = M.util.image_url('i/persona_sign_in_black', 'moodle');
var imagealt = M.util.get_string('signinwithyouremail', 'badges');
var button = Y.Node.create('<img id="persona_signin" src="'+imageurl+'" width="202" height="25" alt="'+imagealt+'"/>');
Y.one('#persona-container').append(button);

// Bind a click event to trigger login when clicked.
button.on('click', function() {
Y.one('#connection-error').empty();
navigator.id.get(badges_handle_assertion);
});
}
51 changes: 28 additions & 23 deletions badges/backpack_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,28 +39,37 @@ class edit_backpack_form extends moodleform {
* Defines the form
*/
public function definition() {
global $USER;
global $USER, $PAGE, $OUTPUT;
$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'), 'http://backpack.openbadges.org');
$status = html_writer::tag('span', get_string('notconnected', 'badges'), array('class' => 'notconnected'));
$mform->addElement('static', 'url', get_string('url'), BADGE_BACKPACKURL);
$status = html_writer::tag('span', get_string('notconnected', 'badges'),
array('class' => 'notconnected', 'id' => 'connection-status'));
$mform->addElement('static', 'status', get_string('status'), $status);

$mform->addElement('text', 'email', get_string('email'), array('size' => '50'));
$mform->setDefault('email', $USER->email);
$mform->setType('email', PARAM_RAW);
$mform->addRule('email', get_string('required'), 'required', null , 'client');
$mform->addHelpButton('email', 'backpackemail', 'badges');
$nojs = html_writer::tag('noscript', get_string('error:personaneedsjs', 'badges'),
array('class' => 'notconnected'));
$personadiv = $OUTPUT->container($nojs, null, 'persona-container');

$mform->addElement('static', 'persona', '', $personadiv);
$mform->addHelpButton('persona', 'personaconnection', 'badges');

$PAGE->requires->js(new moodle_url('https://login.persona.org/include.js'));
$PAGE->requires->js('/badges/backpack.js');
$PAGE->requires->js_init_call('badges_init_persona_login_button', null, false);
$PAGE->requires->strings_for_js(array('error:backpackloginfailed', 'signinwithyouremail',
'error:noassertion', 'error:connectionunknownreason', 'error:badjson', 'connecting',
'notconnected'), 'badges');

$mform->addElement('hidden', 'userid', $USER->id);
$mform->setType('userid', PARAM_INT);

$mform->addElement('hidden', 'backpackurl', 'http://backpack.openbadges.org');
$mform->addElement('hidden', 'backpackurl', BADGE_BACKPACKURL);
$mform->setType('backpackurl', PARAM_URL);

$this->add_action_buttons(true, get_string('connect', 'badges'));
}

/**
Expand All @@ -70,18 +79,14 @@ public function validation($data, $files) {
global $DB;
$errors = parent::validation($data, $files);

if (!validate_email($data['email'])) {
$errors['email'] = get_string('invalidemail');
} else {
$check = new stdClass();
$check->backpackurl = $data['backpackurl'];
$check->email = $data['email'];

$bp = new OpenBadgesBackpackHandler($check);
$request = $bp->curl_request('user');
if (isset($request->status) && $request->status == 'missing') {
$errors['email'] = get_string('error:nosuchuser', 'badges');
}
$check = new stdClass();
$check->backpackurl = $data['backpackurl'];
$check->email = $data['email'];

$bp = new OpenBadgesBackpackHandler($check);
$request = $bp->curl_request('user');
if (isset($request->status) && $request->status == 'missing') {
$errors['email'] = get_string('error:nosuchuser', 'badges');
}
return $errors;
}
Expand Down Expand Up @@ -113,7 +118,7 @@ public function definition() {

$mform->addElement('header', 'backpackheader', get_string('backpackconnection', 'badges'));
$mform->addHelpButton('backpackheader', 'backpackconnection', 'badges');
$mform->addElement('static', 'url', get_string('url'), 'http://backpack.openbadges.org');
$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);
Expand Down
136 changes: 136 additions & 0 deletions badges/backpackconnect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?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/>.

/**
* AJAX script for validating backpack connection.
*
* @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 Simon Coggins <[email protected]>
*/

define('AJAX_SCRIPT', true);

require_once(dirname(dirname(__FILE__)) . '/config.php');
require_once($CFG->dirroot . '/badges/lib/backpacklib.php');
require_once($CFG->libdir . '/filelib.php');

require_sesskey();
require_login();
$PAGE->set_url('/badges/backpackconnect.php');
$PAGE->set_context(context_system::instance());

// Use PHP input filtering as there is no PARAM type for
// the type of cleaning that is required (ASCII chars 32-127 only).
$assertion = filter_input(
INPUT_POST,
'assertion',
FILTER_UNSAFE_RAW,
FILTER_FLAG_STRIP_LOW|FILTER_FLAG_STRIP_HIGH
);

// Audience is the site url scheme + host + port only.
$wwwparts = parse_url($CFG->wwwroot);
$audience = $wwwparts['scheme'] . '://' . $wwwparts['host'];
$audience .= isset($wwwparts['port']) ? $wwwparts['port'] : '';
$params = 'assertion=' . urlencode($assertion) . '&audience=' .
urlencode($audience);

$curl = new curl();
$url = 'https://verifier.login.persona.org/verify';
$options = array(
'FRESH_CONNECT' => true,
'RETURNTRANSFER' => true,
'FORBID_REUSE' => true,
'SSL_VERIFYPEER' => true,
'SSL_VERIFYHOST' => 2,
'HEADER' => 0,
'HTTPHEADER' => array('Content-type: application/x-www-form-urlencoded'),
'CONNECTTIMEOUT' => 0,
'TIMEOUT' => 10, // Fail if data not returned within 10 seconds.
);
$result = $curl->post($url, $params, $options);

// Handle time-out and failed request.
if ($curl->errno != 0) {
if ($curl->errno == CURLE_OPERATION_TIMEOUTED) {
$reason = get_string('error:requesttimeout', 'badges');
} else {
$reason = get_string('error:requesterror', 'badges', $curl->errno);
}
badges_send_response('failure', $reason);
}

$data = json_decode($result);

if (!isset($data->status) || $data->status != 'okay') {
$reason = isset($data->reason) ? $data->reason : get_string('error:connectionunknownreason', 'badges');
badges_send_response('failure', $reason);
}

// Make sure email matches a backpack.
$check = new stdClass();
$check->backpackurl = BADGE_BACKPACKURL;
$check->email = $data->email;

$bp = new OpenBadgesBackpackHandler($check);
$request = $bp->curl_request('user');
if (isset($request->status) && $request->status == 'missing') {
$reason = get_string('error:backpackemailnotfound', 'badges', $data->email);
badges_send_response('failure', $reason);
} else if (empty($request->userId)) {
$reason = get_string('error:backpackdatainvalid', 'badges');
badges_send_response('failure', $reason);
} else {
$backpackuid = $request->userId;
}

// Insert record.
$obj = new stdClass();
$obj->userid = $USER->id;
$obj->email = $data->email;
$obj->backpackurl = BADGE_BACKPACKURL;
$obj->backpackuid = $backpackuid;
$obj->autosync = 0;
$obj->password = '';
$DB->insert_record('badge_backpack', $obj);

// Return success indicator and email address.
badges_send_response('success', $data->email);


/**
* Return a JSON response containing the response provided.
*
* @param string $status Status of the response, typically 'success' or 'failure'.
* @param string $responsetext On success, the email address of the user,
* otherwise a reason for the failure.
* @return void Outputs the JSON and terminates the script.
*/
function badges_send_response($status, $responsetext) {
$out = new stdClass();
$out->status = $status;
if ($status == 'success') {
$out->email = $responsetext;
} else {
$out->reason = $responsetext;
send_header_404();
}
echo json_encode($out);
exit;
}
6 changes: 6 additions & 0 deletions badges/lib/backpacklib.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@

defined('MOODLE_INTERNAL') || die();

/*
* URL of backpack. Currently only the Open Badges backpack
* is supported.
*/
define('BADGE_BACKPACKURL', 'http://backpack.openbadges.org');

global $CFG;
require_once($CFG->libdir . '/filelib.php');

Expand Down
17 changes: 15 additions & 2 deletions lang/en/badges.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,11 @@
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['backpackdetails'] = 'Backpack settings';
$string['backpackemail'] = 'Email address';
$string['backpackemail_help'] = 'Email address associated with your backpack.
$string['backpackemail_help'] = 'The email address associated with your backpack. While you are connected, any badges earned on this site will be associated with this email address.';
$string['personaconnection'] = 'Sign in with your email';
$string['personaconnection_help'] = 'Persona is a system for identifying yourself across the web, using an email address that you own. The Open Badges backpack uses Persona as a login system, so to be able to connect to a backpack you with need a Persona account.
If backpack connection is established, this email address is used instead of your internal email address to push badges to your backpack.';
For more information about Persona visit <a href="https://login.persona.org/about">https://login.persona.org/about</a>.';
$string['backpackimport'] = 'Badge import settings';
$string['backpackimport_help'] = 'After backpack connection is successfully established, badges from your backpack can be displayed on your "My Badges" page and your profile page.
Expand Down Expand Up @@ -128,6 +130,7 @@
$string['configuremessage'] = 'Badge message';
$string['connect'] = 'Connect';
$string['connected'] = 'Connected';
$string['connecting'] = 'Connecting...';
$string['contact'] = 'Contact';
$string['contact_help'] = 'An email address associated with the badge issuer.';
$string['copyof'] = 'Copy of {$a}';
Expand Down Expand Up @@ -202,10 +205,15 @@
$string['donotaward'] = 'Currently, this badge is not active, so it cannot be awarded to users. If you would like to award this badge, please set its status to active.';
$string['editsettings'] = 'Edit settings';
$string['enablebadges'] = 'Enable badges';
$string['error:backpackdatainvalid'] = 'The data return from the backpack was invalid.';
$string['error:backpackemailnotfound'] = 'The email \'{$a}\' is not associated with a backpack. You need to <a href="http://backpack.openbadges.org">create a backpack</a> for that account or sign in with another email address.';
$string['error:backpacknotavailable'] = 'Your site is not accessible from the Internet, so any badges issued from this site cannot be verified by external backpack services.';
$string['error:backpackloginfailed'] = 'You could not be connected to an external backpack for the following reason: {$a}';
$string['error:backpackproblem'] = 'There was a problem connecting to your backpack service provider. Please try again later.';
$string['error:badjson'] = 'The connection attempt returned invalid data.';
$string['error:cannotact'] = 'Cannot activate the badge. ';
$string['error:cannotawardbadge'] = 'Cannot award badge to a user.';
$string['error:connectionunknownreason'] = 'The connection was unsuccessful but no reason was given.';
$string['error:clone'] = 'Cannot clone the badge.';
$string['error:duplicatename'] = 'Badge with such name already exists in the system.';
$string['error:externalbadgedoesntexist'] = 'Badge not found';
Expand All @@ -214,6 +222,7 @@
$string['error:invalidexpiredate'] = 'Expiry date has to be in the future.';
$string['error:invalidexpireperiod'] = 'Expiry period cannot be negative or equal 0.';
$string['error:noactivities'] = 'There are no activities with completion criteria enabled in this course.';
$string['error:noassertion'] = 'No assertion was returned by Persona. You may have closed the dialog before completing the login process.';
$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>';
Expand All @@ -226,6 +235,9 @@
$string['error:nosuchuser'] = 'User with this email address does not have an account with the current backpack provider.';
$string['error:notifycoursedate'] = 'Warning: Badges associated with course and activity completions will not be issued until the course start date.';
$string['error:parameter'] = 'Warning: At least one parameter should be selected to ensure correct badge issuing workflow.';
$string['error:personaneedsjs'] = 'Currently, Javascript is required to connect to your backpack. If you can, enable Javascript and reload the page.';
$string['error:requesttimeout'] = 'The connection request timed out before it could complete.';
$string['error:requesterror'] = 'The connection request failed (error code {$a}).';
$string['error:save'] = 'Cannot save the badge.';
$string['evidence'] = 'Evidence';
$string['existingrecipients'] = 'Existing badge recipients';
Expand Down Expand Up @@ -331,6 +343,7 @@
$string['selectgroup_start'] = 'Select collections from your backpack to display on this site:';
$string['selecting'] = 'With selected badges...';
$string['setup'] = 'Set up connection';
$string['signinwithyouremail'] = 'Sign in with your email';
$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.
Expand Down
Binary file added pix/i/persona_sign_in_black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions theme/base/style/core.css
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,7 @@ div.badge .expireimage { width: 100px; height: 100px; left: 20px; top: 0px; }
.dir-rtl .activatebadge { text-align: right; }
.addcourse { float: right; }
.dir-rtl .addcourse { float: left; }
img#persona_signin { cursor: pointer; }

/**
* The date selector popup.
Expand Down
Loading

0 comments on commit e50df55

Please sign in to comment.