From 748117eb47eec62036ce7061ba5dd41fefb57c75 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Fri, 25 Jan 2019 15:47:49 +0800 Subject: [PATCH] MDL-64702 badges: Add missing data to backup Backup and restore of badges is missing data: * related badges * alignments * endorsement Also added to "clone" (duplicate a badge). --- backup/moodle2/backup_stepslib.php | 28 ++++++- backup/moodle2/restore_stepslib.php | 110 +++++++++++++++++++++++++++- badges/badge_json.php | 4 +- badges/classes/external.php | 2 +- badges/classes/privacy/provider.php | 19 +---- badges/competency.php | 2 +- badges/renderer.php | 4 +- badges/tests/badgeslib_test.php | 6 +- badges/tests/external_test.php | 2 +- badges/tests/privacy_test.php | 3 +- lib/badgeslib.php | 24 +++++- 11 files changed, 172 insertions(+), 32 deletions(-) diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php index f5da034eb8e..b18b2eb1a78 100644 --- a/backup/moodle2/backup_stepslib.php +++ b/backup/moodle2/backup_stepslib.php @@ -794,12 +794,25 @@ class backup_badges_structure_step extends backup_structure_step { $badge = new backup_nested_element('badge', array('id'), array('name', 'description', 'timecreated', 'timemodified', 'usercreated', 'usermodified', 'issuername', 'issuerurl', 'issuercontact', 'expiredate', 'expireperiod', 'type', 'courseid', - 'message', 'messagesubject', 'attachment', 'notification', 'status', 'nextcron')); + 'message', 'messagesubject', 'attachment', 'notification', 'status', 'nextcron', + 'version', 'language', 'imageauthorname', 'imageauthoremail', 'imageauthorurl', + 'imagecaption')); $criteria = new backup_nested_element('criteria'); $criterion = new backup_nested_element('criterion', array('id'), array('badgeid', 'criteriatype', 'method', 'description', 'descriptionformat')); + $endorsement = new backup_nested_element('endorsement', array('id'), array('badgeid', + 'issuername', 'issuerurl', 'issueremail', 'claimid', 'claimcomment', 'dateissued')); + + $alignments = new backup_nested_element('alignments'); + $alignment = new backup_nested_element('alignment', array('id'), array('badgeid', + 'targetname', 'targeturl', 'targetdescription', 'targetframework', 'targetcode')); + + $relatedbadges = new backup_nested_element('relatedbadges'); + $relatedbadge = new backup_nested_element('relatedbadge', array('id'), array('badgeid', + 'relatedbadgeid')); + $parameters = new backup_nested_element('parameters'); $parameter = new backup_nested_element('parameter', array('id'), array('critid', 'name', 'value', 'criteriatype')); @@ -815,6 +828,11 @@ class backup_badges_structure_step extends backup_structure_step { $criteria->add_child($criterion); $criterion->add_child($parameters); $parameters->add_child($parameter); + $badge->add_child($endorsement); + $badge->add_child($alignments); + $alignments->add_child($alignment); + $badge->add_child($relatedbadges); + $relatedbadges->add_child($relatedbadge); $badge->add_child($manual_awards); $manual_awards->add_child($manual_award); @@ -822,6 +840,10 @@ class backup_badges_structure_step extends backup_structure_step { $badge->set_source_table('badge', array('courseid' => backup::VAR_COURSEID)); $criterion->set_source_table('badge_criteria', array('badgeid' => backup::VAR_PARENTID)); + $endorsement->set_source_table('badge_endorsement', array('badgeid' => backup::VAR_PARENTID)); + + $alignment->set_source_table('badge_competencies', array('badgeid' => backup::VAR_PARENTID)); + $relatedbadge->set_source_table('badge_related', array('badgeid' => backup::VAR_PARENTID)); $parametersql = 'SELECT cp.*, c.criteriatype FROM {badge_criteria_param} cp JOIN {badge_criteria} c @@ -838,6 +860,10 @@ class backup_badges_structure_step extends backup_structure_step { $badge->annotate_ids('user', 'usermodified'); $criterion->annotate_ids('badge', 'badgeid'); $parameter->annotate_ids('criterion', 'critid'); + $endorsement->annotate_ids('badge', 'badgeid'); + $alignment->annotate_ids('badge', 'badgeid'); + $relatedbadge->annotate_ids('badge', 'badgeid'); + $relatedbadge->annotate_ids('badge', 'relatedbadgeid'); $badge->annotate_files('badges', 'badgeimage', 'id'); $manual_award->annotate_ids('badge', 'badgeid'); $manual_award->annotate_ids('user', 'recipientid'); diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index 84bf0de8e8b..61f46c52c87 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -2506,6 +2506,9 @@ class restore_badges_structure_step extends restore_structure_step { $paths[] = new restore_path_element('badge', '/badges/badge'); $paths[] = new restore_path_element('criterion', '/badges/badge/criteria/criterion'); $paths[] = new restore_path_element('parameter', '/badges/badge/criteria/criterion/parameters/parameter'); + $paths[] = new restore_path_element('endorsement', '/badges/badge/endorsement'); + $paths[] = new restore_path_element('alignment', '/badges/badge/alignments/alignment'); + $paths[] = new restore_path_element('relatedbadge', '/badges/badge/relatedbadges/relatedbadge'); $paths[] = new restore_path_element('manual_award', '/badges/badge/manual_awards/manual_award'); return $paths; @@ -2550,13 +2553,87 @@ class restore_badges_structure_step extends restore_structure_step { 'attachment' => $data->attachment, 'notification' => $data->notification, 'status' => BADGE_STATUS_INACTIVE, - 'nextcron' => $data->nextcron + 'nextcron' => $data->nextcron, + 'version' => $data->version, + 'language' => $data->language, + 'imageauthorname' => $data->imageauthorname, + 'imageauthoremail' => $data->imageauthoremail, + 'imageauthorurl' => $data->imageauthorurl, + 'imagecaption' => $data->imagecaption ); $newid = $DB->insert_record('badge', $params); $this->set_mapping('badge', $data->id, $newid, $restorefiles); } + /** + * Create an endorsement for a badge. + * + * @param mixed $data + * @return void + */ + public function process_endorsement($data) { + global $DB; + + $data = (object)$data; + + $params = array( + 'badgeid' => $this->get_new_parentid('badge'), + 'issuername' => $data->issuername, + 'issuerurl' => $data->issuerurl, + 'issueremail' => $data->issueremail, + 'claimid' => $data->claimid, + 'claimcomment' => $data->claimcomment, + 'dateissued' => $this->apply_date_offset($data->dateissued) + ); + $newid = $DB->insert_record('badge_endorsement', $params); + $this->set_mapping('endorsement', $data->id, $newid); + } + + /** + * Link to related badges for a badge. This relies on post processing in after_execute(). + * + * @param mixed $data + * @return void + */ + public function process_relatedbadge($data) { + global $DB; + + $data = (object)$data; + $relatedbadgeid = $data->relatedbadgeid; + + if ($relatedbadgeid) { + // Only backup and restore related badges if they are contained in the backup file. + $params = array( + 'badgeid' => $this->get_new_parentid('badge'), + 'relatedbadgeid' => $relatedbadgeid + ); + $newid = $DB->insert_record('badge_related', $params); + } + } + + /** + * Link to an alignment for a badge. + * + * @param mixed $data + * @return void + */ + public function process_alignment($data) { + global $DB; + + $data = (object)$data; + $params = array( + 'badgeid' => $this->get_new_parentid('badge'), + 'targetname' => $data->targetname, + 'targeturl' => $data->targeturl, + 'targetdescription' => $data->targetdescription, + 'targetframework' => $data->targetframework, + 'targetcode' => $data->targetcode + ); + $newid = $DB->insert_record('badge_competencies', $params); + $this->set_mapping('alignment', $data->id, $newid); + } + public function process_criterion($data) { global $DB; @@ -2569,6 +2646,7 @@ class restore_badges_structure_step extends restore_structure_step { 'description' => isset($data->description) ? $data->description : '', 'descriptionformat' => isset($data->descriptionformat) ? $data->descriptionformat : 0, ); + $newid = $DB->insert_record('badge_criteria', $params); $this->set_mapping('criterion', $data->id, $newid); } @@ -2634,8 +2712,38 @@ class restore_badges_structure_step extends restore_structure_step { } protected function after_execute() { + global $DB; // Add related files. $this->add_related_files('badges', 'badgeimage', 'badge'); + + $badgeid = $this->get_new_parentid('badge'); + // Remap any related badges. + // We do this in the DB directly because this is backup/restore it is not valid to call into + // the component API. + $params = array('badgeid' => $badgeid); + $query = "SELECT DISTINCT br.id, br.badgeid, br.relatedbadgeid + FROM {badge_related} br + WHERE (br.badgeid = :badgeid)"; + $relatedbadges = $DB->get_records_sql($query, $params); + $newrelatedids = []; + foreach ($relatedbadges as $relatedbadge) { + $relatedid = $this->get_mappingid('badge', $relatedbadge->relatedbadgeid); + $params['relatedbadgeid'] = $relatedbadge->relatedbadgeid; + $DB->delete_records_select('badge_related', '(badgeid = :badgeid AND relatedbadgeid = :relatedbadgeid)', $params); + if ($relatedid) { + $newrelatedids[] = $relatedid; + } + } + if (!empty($newrelatedids)) { + $relatedbadges = []; + foreach ($newrelatedids as $relatedid) { + $relatedbadge = new stdClass(); + $relatedbadge->badgeid = $badgeid; + $relatedbadge->relatedbadgeid = $relatedid; + $relatedbadges[] = $relatedbadge; + } + $DB->insert_records('badge_related', $relatedbadges); + } } } diff --git a/badges/badge_json.php b/badges/badge_json.php index 907f0f10dc9..0985a519791 100644 --- a/badges/badge_json.php +++ b/badges/badge_json.php @@ -95,7 +95,7 @@ if ($badge->status != BADGE_STATUS_INACTIVE) { $json['endorsement'] = $endorsementurl->out(false); } - $competencies = $badge->get_alignment(); + $competencies = $badge->get_alignments(); if (!empty($competencies)) { foreach ($competencies as $item) { $alignment = array('targetName' => $item->targetname, 'targetUrl' => $item->targeturl); @@ -123,4 +123,4 @@ if ($badge->status != BADGE_STATUS_INACTIVE) { $json['error'] = get_string('error:relatedbadgedoesntexist', 'badges'); } echo $OUTPUT->header(); -echo json_encode($json); \ No newline at end of file +echo json_encode($json); diff --git a/badges/classes/external.php b/badges/classes/external.php index 15437d09c2f..4c6759a70a1 100644 --- a/badges/classes/external.php +++ b/badges/classes/external.php @@ -150,7 +150,7 @@ class core_badges_external extends external_api { // Create a badge instance to be able to get the endorsement and other info. $badgeinstance = new badge($badge->id); $endorsement = $badgeinstance->get_endorsement(); - $competencies = $badgeinstance->get_alignment(); + $competencies = $badgeinstance->get_alignments(); $relatedbadges = $badgeinstance->get_related_badges(); if (!$canconfiguredetails) { diff --git a/badges/classes/privacy/provider.php b/badges/classes/privacy/provider.php index f36aa4f927f..689a9fe192f 100644 --- a/badges/classes/privacy/provider.php +++ b/badges/classes/privacy/provider.php @@ -82,23 +82,6 @@ class provider implements 'datemet' => 'privacy:metadata:criteriamet:datemet', ], 'privacy:metadata:criteriamet'); - $collection->add_database_table('badge_endorsement', [ - 'issuername' => 'privacy:metadata:endorsement:issuername', - 'issuerurl' => 'privacy:metadata:endorsement:issuerurl', - 'issueremail' => 'privacy:metadata:endorsement:issueremail', - 'claimid' => 'privacy:metadata:endorsement:claimid', - 'claimcomment' => 'privacy:metadata:endorsement:claimcomment', - 'dateissued' => 'privacy:metadata:endorsement:dateissued', - ], 'privacy:metadata:endorsement'); - - $collection->add_database_table('badge_competencies', [ - 'targetname' => 'privacy:metadata:alignment:targetname', - 'targeturl' => 'privacy:metadata:alignment:targeturl', - 'targetdescription' => 'privacy:metadata:alignment:targetdescription', - 'targetframework' => 'privacy:metadata:alignment:targetframework', - 'targetcode' => 'privacy:metadata:alignment:targetcode', - ], 'privacy:metadata:alignment'); - $collection->add_database_table('badge_manual_award', [ 'recipientid' => 'privacy:metadata:manualaward:recipientid', 'issuerid' => 'privacy:metadata:manualaward:issuerid', @@ -393,7 +376,7 @@ class provider implements // Export the badges. $uniqueid = $DB->sql_concat_join("'-'", ['b.id', 'COALESCE(bc.id, 0)', 'COALESCE(bi.id, 0)', - 'COALESCE(bma.id, 0)', 'COALESCE(bcm.id, 0)', 'COALESCE(ba.id, 0)']); + 'COALESCE(bma.id, 0)', 'COALESCE(bcm.id, 0)', 'COALESCE(brb.id, 0)', 'COALESCE(ba.id, 0)']); $sql = " SELECT $uniqueid AS uniqueid, b.id, bi.id AS biid, bi.dateissued, bi.dateexpire, bi.uniquehash, diff --git a/badges/competency.php b/badges/competency.php index 1eae429f2d2..d8a9697b288 100644 --- a/badges/competency.php +++ b/badges/competency.php @@ -97,7 +97,7 @@ if ($alignmentid || $action == 'add' || $action == 'edit') { $urlbutton = new moodle_url('/badges/competency.php', array('id' => $badge->id, 'action' => 'add')); echo $OUTPUT->box($OUTPUT->single_button($urlbutton, get_string('addalignment', 'badges')), 'clearfix mdl-align'); } - $alignment = $badge->get_alignment(); + $alignment = $badge->get_alignments(); if (count($alignment) > 0) { $renderrelated = new badge_competencies_alignment($alignment, $badgeid); echo $output->render($renderrelated); diff --git a/badges/renderer.php b/badges/renderer.php index b88d940a31c..bff3e5a1907 100644 --- a/badges/renderer.php +++ b/badges/renderer.php @@ -428,7 +428,7 @@ class core_badges_renderer extends plugin_renderer_base { } $output .= html_writer::alist($items, array(), 'ul'); } - $competencies = $badge->get_alignment(); + $competencies = $badge->get_alignments(); if (!empty($competencies)) { $output .= $this->heading(get_string('alignment', 'badges'), 3); $items = array(); @@ -1106,7 +1106,7 @@ class core_badges_renderer extends plugin_renderer_base { protected function print_badge_competencies(badge $badge) { $output = ''; $output .= $this->heading(get_string('alignment', 'badges'), 3); - $competencies = $badge->get_alignment(); + $competencies = $badge->get_alignments(); if (!empty($competencies)) { $items = array(); foreach ($competencies as $competency) { diff --git a/badges/tests/badgeslib_test.php b/badges/tests/badgeslib_test.php index d577c787155..f141a71f321 100644 --- a/badges/tests/badgeslib_test.php +++ b/badges/tests/badgeslib_test.php @@ -813,7 +813,7 @@ class core_badges_badgeslib_testcase extends advanced_testcase { $competency2 = clone $competency1; $newid1 = $badge->save_alignment($competency1); $newid2 = $badge->save_alignment($competency2); - $competencies1 = $badge->get_alignment(); + $competencies1 = $badge->get_alignments(); $this->assertCount(2, $competencies1); $this->assertEquals($competency1->badgeid, $competencies1[$newid1]->badgeid); @@ -826,12 +826,12 @@ class core_badges_badgeslib_testcase extends advanced_testcase { // Update competency aligment. $competencies1[$newid1]->targetname = 'CCSS.ELA-Literacy.RST.11-12.3 update'; $badge->save_alignment($competencies1[$newid1], $competencies1[$newid1]->id); - $competencies2 = $badge->get_alignment(); + $competencies2 = $badge->get_alignments(); $this->assertEquals($competencies1[$newid1]->id, $competencies2[$newid1]->id); $this->assertEquals($competencies1[$newid1]->targetname, $competencies2[$newid1]->targetname); // Delete competency alignment. $badge->delete_alignment($competencies1[$newid2]->id); - $this->assertCount(1, $badge->get_alignment()); + $this->assertCount(1, $badge->get_alignments()); } } diff --git a/badges/tests/external_test.php b/badges/tests/external_test.php index 9ebf174980a..1f4e51e0559 100644 --- a/badges/tests/external_test.php +++ b/badges/tests/external_test.php @@ -165,7 +165,7 @@ class core_badges_external_testcase extends externallib_advanced_testcase { // Get the endorsement, competencies and related badges. $badgeinstance = new badge($badge->id); $endorsement = $badgeinstance->get_endorsement(); - $competencies = $badgeinstance->get_alignment(); + $competencies = $badgeinstance->get_alignments(); $relatedbadges = $badgeinstance->get_related_badges(); $badge->competencies = array(); $badge->relatedbadges = array(); diff --git a/badges/tests/privacy_test.php b/badges/tests/privacy_test.php index 4e93126790c..5daa5cf5299 100644 --- a/badges/tests/privacy_test.php +++ b/badges/tests/privacy_test.php @@ -650,7 +650,8 @@ class core_badges_privacy_testcase extends provider_testcase { /** * Relate a badge. * - * @param array $params Parameters. + * @param int $badgeid The badge ID. + * @param int $relatedbadgeid The related badge ID. * @return object */ protected function relate_badge(int $badgeid, int $relatedbadgeid) { diff --git a/lib/badgeslib.php b/lib/badgeslib.php index d0c1931c428..f1acbda292f 100644 --- a/lib/badgeslib.php +++ b/lib/badgeslib.php @@ -315,6 +315,28 @@ class badge { $crit->make_clone($new); } + // Copy endorsement. + $endorsement = $this->get_endorsement(); + if ($endorsement) { + unset($endorsement->id); + $endorsement->badgeid = $new; + $newbadge->save_endorsement($endorsement); + } + + // Copy alignments. + $alignments = $this->get_alignments(); + foreach ($alignments as $alignment) { + unset($alignment->id); + $alignment->badgeid = $new; + $newbadge->save_alignment($alignment); + } + + // Copy related badges. + $related = $this->get_related_badges(true); + if (!empty($related)) { + $newbadge->add_related_badges(array_keys($related)); + } + // Trigger event, badge duplicated. $eventparams = array('objectid' => $new, 'context' => $PAGE->context); $event = \core\event\badge_duplicated::create($eventparams); @@ -838,7 +860,7 @@ class badge { * * @return array List content competencies. */ - public function get_alignment() { + public function get_alignments() { global $DB; return $DB->get_records('badge_competencies', array('badgeid' => $this->id)); }