From 3cae9421a892b07251523f9f7f368eac9ed0885e Mon Sep 17 00:00:00 2001 From: Peter Dias Date: Fri, 28 Feb 2020 13:00:15 +0800 Subject: [PATCH] MDL-65959 badges: Update the implementation to use admin set backpack --- badges/classes/backpack_api.php | 8 +-- badges/classes/form/backpack.php | 63 ++++++++++++----------- badges/classes/form/external_backpack.php | 48 +++++++++++++---- badges/mybackpack.php | 5 +- lang/en/badges.php | 6 ++- lang/en/deprecated.txt | 1 + lib/badgeslib.php | 18 ++++--- lib/db/install.xml | 4 +- lib/db/upgrade.php | 36 +++++++++++-- 9 files changed, 125 insertions(+), 64 deletions(-) diff --git a/badges/classes/backpack_api.php b/badges/classes/backpack_api.php index d15544340f9..7a8c0982a2f 100644 --- a/badges/classes/backpack_api.php +++ b/badges/classes/backpack_api.php @@ -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; } diff --git a/badges/classes/form/backpack.php b/badges/classes/form/backpack.php index 6689a4f7ac0..6310b4cdd9a 100644 --- a/badges/classes/form/backpack.php +++ b/badges/classes/form/backpack.php @@ -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'); diff --git a/badges/classes/form/external_backpack.php b/badges/classes/form/external_backpack.php index 6fefec49cfb..32390fd5f6c 100644 --- a/badges/classes/form/external_backpack.php +++ b/badges/classes/form/external_backpack.php @@ -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'); + } + } } diff --git a/badges/mybackpack.php b/badges/mybackpack.php index 11ce5ccede4..e75972726c8 100644 --- a/badges/mybackpack.php +++ b/badges/mybackpack.php @@ -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), diff --git a/lang/en/badges.php b/lang/en/badges.php index a98db31c5ec..51b61a6efcc 100644 --- a/lang/en/badges.php +++ b/lang/en/badges.php @@ -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.'; diff --git a/lang/en/deprecated.txt b/lang/en/deprecated.txt index 4ba2ea50b9e..1028bfe8b3f 100644 --- a/lang/en/deprecated.txt +++ b/lang/en/deprecated.txt @@ -124,3 +124,4 @@ userfilterplaceholder,core sitebackpackverify,core_badges filetypesnotwhitelisted,core_form modeloutputdirinfo,core_analytics +backpackneedsupdate,core_badges diff --git a/lib/badgeslib.php b/lib/badgeslib.php index 1fc0b46d442..38e42dca437 100644 --- a/lib/badgeslib.php +++ b/lib/badgeslib.php @@ -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) { diff --git a/lib/db/install.xml b/lib/db/install.xml index 478146184cc..22258604309 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -1,5 +1,5 @@ - @@ -3188,6 +3188,7 @@ + @@ -3275,7 +3276,6 @@ - diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index ae5aa60495d..7da92b52b2a 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -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. -- 2.43.0