MDL-65959 badges: Update the implementation to use admin set backpack

This commit is contained in:
Peter Dias 2020-02-28 13:00:15 +08:00
parent b6435e0934
commit 3cae9421a8
9 changed files with 125 additions and 64 deletions

View File

@ -90,13 +90,10 @@ class backpack_api {
$this->backpackapiurl = $sitebackpack->backpackapiurl;
$this->backpackapiversion = $sitebackpack->apiversion;
$this->password = $sitebackpack->password;
$this->email = !empty($CFG->badges_defaultissuercontact) ? $CFG->badges_defaultissuercontact : '';
$this->email = $sitebackpack->backpackemail;
$this->isuserbackpack = false;
$this->backpackid = $sitebackpack->id;
if (!empty($userbackpack)) {
if ($userbackpack->externalbackpackid != $sitebackpack->id) {
throw new coding_exception('Incorrect backpack');
}
$this->isuserbackpack = true;
$this->password = $userbackpack->password;
$this->email = $userbackpack->email;
@ -602,7 +599,7 @@ class backpack_api {
* @param integer $backpackid The backpack to disconnect
* @return boolean
*/
public function disconnect_backpack($userid, $backpackid, $externalbackupid) {
public function disconnect_backpack($userid, $backpackid) {
global $DB, $USER;
if (\core\session\manager::is_loggedinas() || $userid != $USER->id) {
@ -614,7 +611,6 @@ class backpack_api {
$DB->delete_records('badge_external', array('backpackid' => $backpackid));
$DB->delete_records('badge_backpack', array('userid' => $userid));
$DB->delete_records('badge_external_backpack', array('id' => $externalbackupid));
$badgescache->delete($userid);
return true;
}

View File

@ -47,31 +47,55 @@ class backpack extends external_backpack {
public function definition() {
global $USER, $PAGE, $OUTPUT, $CFG;
$mform = $this->_form;
$this->_customdata['userbackpack'] = 1;
$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('hidden', 'userid', $USER->id);
$mform->setType('userid', PARAM_INT);
$mform->addElement('hidden', 'externalbackpackid');
$mform->setType('externalbackpackid', PARAM_INT);
$freeze = [];
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->hardFreeze();
$freeze = ['backpackemail'];
$mform->addElement('hidden', 'password', $this->_customdata['backpackpassword']);
$mform->setType('password', PARAM_RAW);
$mform->addElement('hidden', 'externalbackpackid', $this->_customdata['backpackid']);
$mform->setType('externalbackpackid', PARAM_INT);
$status = html_writer::tag('span', get_string('backpackemailverificationpending', 'badges'),
array('class' => 'notconnected', 'id' => 'connection-status'));
} else {
$sitebackpacks = badges_get_site_backpacks();
$choices = [];
$restrictedoptions = [];
foreach ($sitebackpacks as $backpack) {
$choices[$backpack->id] = $backpack->backpackweburl;
if ($backpack->apiversion == OPEN_BADGES_V2P1) {
$restrictedoptions[] = $backpack->id;
}
}
$mform->addElement('select', 'externalbackpackid', get_string('backpackprovider', 'badges'), $choices);
$mform->setType('externalbackpackid', PARAM_INT);
$mform->setDefault('externalbackpackid', $CFG->badges_site_backpack);
$mform->hideIf('password', 'externalbackpackid', 'in', $restrictedoptions);
$mform->hideIf('backpackemail', 'externalbackpackid', 'in', $restrictedoptions);
$status = html_writer::tag('span', get_string('notconnected', 'badges'),
array('class' => 'notconnected', 'id' => 'connection-status'));
}
$mform->addElement('static', 'status', get_string('status'), $status);
parent::definition();
$this->add_auth_fields($this->_customdata['email'] ?? $USER->email, !isset($this->_customdata['email']));
$mform->setDefault('backpackemail', $USER->email);
$mform->setDisableShortforms(false);
// Freeze any elemnts after definition.
if ($freeze) {
$mform->freeze($freeze);
}
$this->add_action_buttons();
}
/**
@ -81,8 +105,8 @@ class backpack extends external_backpack {
* @param null|text $submitlabel
*/
public function add_action_buttons($cancel = true, $submitlabel = null) {
if ($this->_customdata['email']) {
$mform = $this->_form;
$mform = $this->_form;
if (isset($this->_customdata['email'])) {
$buttonarray = [];
$buttonarray[] = &$mform->createElement('submit', 'submitbutton',
get_string('backpackconnectionresendemail', 'badges'));
@ -92,27 +116,6 @@ class backpack extends external_backpack {
$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', get_string('backpackweburl', 'badges'), $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);
if (badges_open_badges_backpack_api() != OPEN_BADGES_V2P1) {
$mform->addElement('text', 'email', get_string('email'), 'maxlength="100" size="30"');
$mform->addHelpButton('email', 'backpackemail', 'badges');
$mform->addRule('email', get_string('required'), 'required', null, 'client');
$mform->setType('email', PARAM_EMAIL);
if (badges_open_badges_backpack_api() == OPEN_BADGES_V2) {
$mform->addElement('passwordunmask', 'backpackpassword', get_string('password'));
$mform->setType('backpackpassword', PARAM_RAW);
} else {
$mform->addElement('hidden', 'backpackpassword', '');
$mform->setType('backpackpassword', PARAM_RAW);
}
}
parent::add_action_buttons(false, get_string('backpackconnectionconnect', 'badges'));
}
}
@ -132,9 +135,9 @@ class backpack extends external_backpack {
$check = new stdClass();
$check->email = $data['backpackemail'];
$check->password = $data['password'];
$check->externalbackpackid = $data['externalbackpackid'];
$sitebackpack = badges_get_site_backpack($data['externalbackpackid']);
$bp = new \core_badges\backpack_api($sitebackpack, $check);
$bp = new \core_badges\backpack_api((object) $data, $check);
$result = $bp->authenticate();
if ($result === false || !empty($result->error)) {
$errors['backpackemail'] = get_string('backpackconnectionunexpectedresult', 'badges');

View File

@ -72,11 +72,11 @@ class external_backpack extends \moodleform {
$mform->addElement('hidden', 'id', ($backpack->id ?? null));
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'badgebackpack', 0);
$mform->setType('badgebackpack', PARAM_INTEGER);
$mform->setType('badgebackpack', PARAM_INT);
$mform->addElement('hidden', 'userid', 0);
$mform->setType('userid', PARAM_INTEGER);
$mform->setType('userid', PARAM_INT);
$mform->addElement('hidden', 'backpackuid', 0);
$mform->setType('backpackuid', PARAM_INTEGER);
$mform->setType('backpackuid', PARAM_INT);
$mform->addElement('advcheckbox', 'includeauthdetails', null, get_string('includeauthdetails', 'core_badges'));
if (!empty($backpack->backpackemail) || !empty($backpack->password)) {
@ -84,14 +84,7 @@ class external_backpack extends \moodleform {
}
$issuercontact = $CFG->badges_defaultissuercontact;
$mform->addElement('text', 'backpackemail', get_string('defaultissuercontact', 'core_badges'));
$mform->setType('backpackemail', PARAM_EMAIL);
$mform->setDefault('backpackemail', $issuercontact);
$mform->addElement('passwordunmask', 'password', get_string('defaultissuerpassword', 'core_badges'));
$mform->setType('password', PARAM_RAW);
$mform->addHelpButton('password', 'defaultissuerpassword', 'badges');
$mform->hideIf('password', 'apiversion', 'neq', 2);
$this->add_auth_fields($issuercontact);
$oauth2options = badges_get_oauth2_service_options();
$mform->addElement('select', 'oauth2_issuerid', get_string('oauth2issuer', 'core_badges'), $oauth2options);
@ -102,6 +95,12 @@ class external_backpack extends \moodleform {
$this->set_data($backpack);
}
$mform->hideIf('includeauthdetails', 'apiversion', 'in', [OPEN_BADGES_V2P1]);
$mform->hideIf('backpackemail', 'includeauthdetails');
$mform->hideIf('backpackemail', 'apiversion', 'in', [OPEN_BADGES_V2P1]);
$mform->hideIf('password', 'includeauthdetails');
$mform->hideIf('password', 'apiversion', 'in', [1, OPEN_BADGES_V2P1]);
// Disable short forms.
$mform->setDisableShortforms();
@ -151,4 +150,31 @@ class external_backpack extends \moodleform {
return $data;
}
/**
* Add backpack specific auth details.
*
* @param string|null $email The email addressed provided or null if it's new.
* @param boolean|true $includepassword Include the password field.
* @throws \coding_exception
*/
protected function add_auth_fields(?string $email, bool $includepassword = true) {
$mform = $this->_form;
$emailstring = get_string('email');
$passwordstring = get_string('password');
if (!isset($this->_customdata['userbackpack'])) {
$emailstring = get_string('defaultissuercontact', 'core_badges');
$passwordstring = get_string('defaultissuerpassword', 'core_badges');
}
$mform->addElement('text', 'backpackemail', $emailstring);
$mform->setType('backpackemail', PARAM_EMAIL);
$mform->setDefault('backpackemail', $email);
if ($includepassword) {
$mform->addElement('passwordunmask', 'password', $passwordstring);
$mform->setType('password', PARAM_RAW);
$mform->addHelpButton('password', 'defaultissuerpassword', 'badges');
}
}
}

View File

@ -64,7 +64,7 @@ if ($disconnect && $backpack) {
} else {
// If backpack is connected, need to select collections.
$bp = new \core_badges\backpack_api($sitebackpack, $backpack);
$bp->disconnect_backpack($USER->id, $backpack->id, $sitebackpack->id);
$bp->disconnect_backpack($USER->id, $backpack->id);
redirect(new moodle_url('/badges/mybackpack.php'));
}
}
@ -142,8 +142,7 @@ if ($backpack) {
badges_disconnect_user_backpack($USER->id);
redirect(new moodle_url('/badges/mybackpack.php'));
} else if (isset($data->backpackemail)) {
$newid = badges_create_site_backpack($data, true);
if (badges_send_verification_email($data->backpackemail, $newid, $data->password)) {
if (badges_send_verification_email($data->backpackemail, $data->externalbackpackid, $data->password)) {
$a = get_user_preferences('badges_email_verify_backpackid');
redirect(new moodle_url('/badges/mybackpack.php'),
get_string('backpackemailverifypending', 'badges', $data->backpackemail),

View File

@ -83,7 +83,6 @@ $string['awards'] = 'Recipients';
$string['backpackavailability'] = 'External badge verification';
$string['backpackconnectionok'] = 'Backpack connection successfully established';
$string['backpackconnectionnottested'] = 'The connection cannot be tested for this backpack because only Open Badges v2.0 backpacks support it.';
$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?**
@ -137,6 +136,7 @@ In this area, you can select collections of badges from your backpack that you w
$string['backpacksettings'] = 'Backpack settings';
$string['backpackapiurl'] = 'Backpack API URL';
$string['backpackweburl'] = 'Backpack URL';
$string['backpackprovider'] = 'Backpack Provider';
$string['badges'] = 'Badges';
$string['badgedetails'] = 'Badge details';
$string['badgeimage'] = 'Image';
@ -571,7 +571,11 @@ $string['version'] = 'Version';
$string['version_help'] = 'The version field may be used to keep track of the badge\'s development. If specified, the version is displayed on the badge page.';
$string['warnexpired'] = ' (This badge has expired!)';
$string['year'] = 'Year(s)';
$string['includeauthdetails'] = "Include authentication details with the backpack";
// Deprecated since Moodle 3.9.
$string['editsettings'] = 'Edit settings';
$string['sitebackpackverify'] = 'Backpack connection';
// Deprecated since Moodle 3.10.
$string['backpackneedsupdate'] = 'The backpack connected to this profile does not match the backpack for the site. You need to disconnect and reconnect the backpack.';

View File

@ -124,3 +124,4 @@ userfilterplaceholder,core
sitebackpackverify,core_badges
filetypesnotwhitelisted,core_form
modeloutputdirinfo,core_analytics
backpackneedsupdate,core_badges

View File

@ -851,9 +851,7 @@ function badges_save_external_backpack(stdClass $data) {
$backpack->apiversion = $data->apiversion;
$backpack->backpackweburl = $data->backpackweburl;
$backpack->backpackapiurl = $data->backpackapiurl;
$backpack->backpackemail = $data->backpackemail;
$backpack->password = !empty($data->password) ? $data->password : '';
$backpack->oauth2_issuerid = !empty($data->oauth2_issuerid) ? $data->oauth2_issuerid : '';
$backpack->oauth2_issuerid = $data->oauth2_issuerid ?? '';
if (isset($data->sortorder)) {
$backpack->sortorder = $data->sortorder;
}
@ -868,7 +866,6 @@ function badges_save_external_backpack(stdClass $data) {
unset($data->id);
badges_save_backpack_credentials($data);
return $data->externalbackpackid;
}
@ -933,7 +930,12 @@ function badges_open_badges_backpack_api() {
function badges_get_site_backpack($id) {
global $DB;
return $DB->get_record('badge_external_backpack', ['id' => $id]);
$sql = "SELECT beb.*, bb.id AS badgebackpack, bb.password, bb.email AS backpackemail
FROM {badge_external_backpack} beb
LEFT JOIN {badge_backpack} bb ON bb.externalbackpackid = beb.id AND bb.userid=:userid
WHERE beb.id=:id";
return $DB->get_record_sql($sql, ['id' => $id, 'userid' => 0]);
}
/**
@ -955,11 +957,11 @@ function badges_get_site_primary_backpack() {
function badges_get_site_backpacks() {
global $DB, $CFG;
$sql = "SELECT beb.*
$sql = "SELECT beb.*, bb.id as badgebackpack, bb.password, bb.email as backpackemail
FROM {badge_external_backpack} beb
LEFT JOIN {badge_backpack} bb ON bb.externalbackpackid = beb.id
WHERE bb.id IS NULL";
$all = $DB->get_records_sql($sql);
WHERE bb.id IS NULL OR bb.userid=:userid";
$all = $DB->get_records_sql($sql, ['userid' => 0]);
foreach ($all as $key => $bp) {
if ($bp->id == $CFG->badges_site_backpack) {

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/db" VERSION="20201007" COMMENT="XMLDB file for core Moodle tables"
<XMLDB PATH="lib/db" VERSION="20201008" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
@ -3188,6 +3188,7 @@
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="fk_userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
<KEY NAME="backpackcredentials" TYPE="unique" FIELDS="userid, externalbackpackid"/>
<KEY NAME="externalbackpack" TYPE="foreign" FIELDS="externalbackpackid" REFTABLE="badge_external_backpack" REFFIELDS="id"/>
</KEYS>
</TABLE>
@ -3275,7 +3276,6 @@
<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."/>
<FIELD NAME="oauth2_issuerid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="OAuth 2 Issuer"/>
</FIELDS>
<KEYS>

View File

@ -2863,10 +2863,40 @@ function xmldb_main_upgrade($oldversion) {
}
if ($oldversion < 2021052500.32) {
global $DB, $CFG;
$table = new xmldb_table('badge_backpack');
$uniquekey = new xmldb_key('backpackcredentials', XMLDB_KEY_UNIQUE, ['userid', 'externalbackpackid']);
// All external backpack providers/hosts are now exclusively stored in badge_external_backpack.
// All credentials are stored in badge_backpack and are unique per user, backpack.
if (!$dbman->find_key_name($table, $uniquekey)) {
$dbman->add_key($table, $uniquekey);
}
// If there is a current backpack set then copy it across to the new structure.
if ($CFG->badges_defaultissuercontact) {
// Get the currently used site backpacks.
$records = $DB->get_records_select('badge_external_backpack', "password IS NOT NULL AND password != ''");
$backpack = [
'userid' => '0',
'email' => $CFG->badges_defaultissuercontact,
'backpackuid' => -1
];
// Create records corresponding to the site backpacks.
foreach ($records as $record) {
$backpack['password'] = $record->password;
$backpack['externalbackpackid'] = $record->id;
$DB->insert_record('badge_backpack', (object) $backpack);
}
}
// Drop the password field as this is moved to badge_backpack.
$table = new xmldb_table('badge_external_backpack');
$field = new xmldb_field('backpackemail', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
$field = new xmldb_field('password', XMLDB_TYPE_CHAR, '50');
if ($dbman->field_exists($table, $field)) {
$dbman->drop_field($table, $field);
}
// Main savepoint reached.