Merge branch 'MDL-68567-master' of git://github.com/sarjona/moodle
authorJake Dallimore <jake@moodle.com>
Mon, 25 May 2020 03:36:48 +0000 (11:36 +0800)
committerJake Dallimore <jake@moodle.com>
Mon, 25 May 2020 03:36:48 +0000 (11:36 +0800)
17 files changed:
badges/amd/build/backpackactions.min.js [new file with mode: 0644]
badges/amd/build/backpackactions.min.js.map [new file with mode: 0644]
badges/amd/build/selectors.min.js [new file with mode: 0644]
badges/amd/build/selectors.min.js.map [new file with mode: 0644]
badges/amd/src/backpackactions.js [new file with mode: 0644]
badges/amd/src/selectors.js [new file with mode: 0644]
badges/backpacks.php
badges/classes/form/external_backpack.php
badges/classes/helper.php [new file with mode: 0644]
badges/classes/output/external_backpacks_page.php
badges/templates/external_backpacks_page.mustache
badges/tests/badgeslib_test.php
badges/tests/behat/backpack.feature
badges/tests/privacy_test.php
lang/en/badges.php
lang/en/deprecated.txt
lib/badgeslib.php

diff --git a/badges/amd/build/backpackactions.min.js b/badges/amd/build/backpackactions.min.js
new file mode 100644 (file)
index 0000000..659e07c
Binary files /dev/null and b/badges/amd/build/backpackactions.min.js differ
diff --git a/badges/amd/build/backpackactions.min.js.map b/badges/amd/build/backpackactions.min.js.map
new file mode 100644 (file)
index 0000000..52bdc23
Binary files /dev/null and b/badges/amd/build/backpackactions.min.js.map differ
diff --git a/badges/amd/build/selectors.min.js b/badges/amd/build/selectors.min.js
new file mode 100644 (file)
index 0000000..dbe7b36
Binary files /dev/null and b/badges/amd/build/selectors.min.js differ
diff --git a/badges/amd/build/selectors.min.js.map b/badges/amd/build/selectors.min.js.map
new file mode 100644 (file)
index 0000000..856297c
Binary files /dev/null and b/badges/amd/build/selectors.min.js.map differ
diff --git a/badges/amd/src/backpackactions.js b/badges/amd/src/backpackactions.js
new file mode 100644 (file)
index 0000000..5730b84
--- /dev/null
@@ -0,0 +1,89 @@
+// 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/>.
+
+/**
+ * Action methods related to backpacks.
+ *
+ * @module     core_badges/backpackactions
+ * @package    core_badges
+ * @copyright  2020 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+import $ from 'jquery';
+import selectors from 'core_badges/selectors';
+import {get_string as getString} from 'core/str';
+import Pending from 'core/pending';
+import ModalFactory from 'core/modal_factory';
+import ModalEvents from 'core/modal_events';
+import Config from 'core/config';
+
+/**
+ * Set up the actions.
+ *
+ * @method init
+ */
+export const init = () => {
+    const pendingPromise = new Pending();
+
+    const root = $(selectors.elements.main);
+    registerListenerEvents(root);
+
+    pendingPromise.resolve();
+};
+
+/**
+ * Register backpack related event listeners.
+ *
+ * @method registerListenerEvents
+ * @param {Object} root The root element.
+ */
+const registerListenerEvents = (root) => {
+
+    root.on('click', selectors.actions.deletebackpack, async(e) => {
+        e.preventDefault();
+
+        const link = $(e.currentTarget);
+        const modal = await buildModal(link);
+
+        displayModal(modal, link);
+    });
+};
+
+const buildModal = async(link) => {
+
+    const backpackurl = link.closest(selectors.elements.backpackurl).attr('data-backpackurl');
+
+    return ModalFactory.create({
+        title: await getString('delexternalbackpack', 'core_badges'),
+        body: await getString('delexternalbackpackconfirm', 'core_badges', backpackurl),
+        type: ModalFactory.types.SAVE_CANCEL,
+    });
+
+};
+
+const displayModal = async(modal, link) => {
+    modal.setSaveButtonText(await getString('delete', 'core'));
+
+    modal.getRoot().on(ModalEvents.save, function() {
+        window.location.href = link.attr('href') + '&sesskey=' + Config.sesskey + '&confirm=1';
+    });
+
+    modal.getRoot().on(ModalEvents.hidden, function() {
+        modal.destroy();
+    });
+
+    modal.show();
+};
diff --git a/badges/amd/src/selectors.js b/badges/amd/src/selectors.js
new file mode 100644 (file)
index 0000000..fea6dfb
--- /dev/null
@@ -0,0 +1,46 @@
+// 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/>.
+
+/**
+ * Define all of the selectors we will be using on the backpack interface.
+ *
+ * @module     core_badges/selectors
+ * @package    core_badges
+ * @copyright  2020 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * A small helper function to build queryable data selectors.
+ *
+ * @method getDataSelector
+ * @param {String} name
+ * @param {String} value
+ * @return {string}
+ */
+const getDataSelector = (name, value) => {
+    return `[data-${name}="${value}"]`;
+};
+
+export default {
+    actions: {
+        deletebackpack: getDataSelector('action', 'deletebackpack'),
+    },
+    elements: {
+        clearsearch: '.input-group-append .clear-icon',
+        main: '#backpacklist',
+        backpackurl: '[data-backpackurl]',
+    },
+};
index 341d511..ae63fd6 100644 (file)
@@ -34,6 +34,7 @@ $output = $PAGE->get_renderer('core', 'badges');
 
 $id = optional_param('id', 0, PARAM_INT);
 $action = optional_param('action', '', PARAM_ALPHA);
+$confirm = optional_param('confirm', 1, PARAM_BOOL);
 
 $PAGE->set_pagelayout('admin');
 $url = new moodle_url('/badges/backpacks.php');
@@ -45,6 +46,18 @@ if (empty($CFG->badges_allowexternalbackpack)) {
 $PAGE->set_url($url);
 $PAGE->set_title(get_string('managebackpacks', 'badges'));
 $PAGE->set_heading($SITE->fullname);
+
+$msg = '';
+$msgtype = 'error';
+if ($action == 'delete' && $confirm && confirm_sesskey()) {
+    if (badges_delete_site_backpack($id)) {
+        $msg = get_string('sitebackpackdeleted', 'badges');
+        $msgtype = 'notifysuccess';
+    } else {
+        $msg = get_string('sitebackpacknotdeleted', 'badges');
+    }
+}
+
 if ($action == 'edit') {
     $backpack = null;
     if (!empty($id)) {
@@ -71,6 +84,9 @@ if ($action == 'edit') {
     echo $OUTPUT->header();
     echo $output->heading(get_string('managebackpacks', 'badges'));
 
+    if ($msg) {
+        echo $OUTPUT->notification($msg, $msgtype);
+    }
     $page = new \core_badges\output\external_backpacks_page($url);
     echo $output->render($page);
 }
index 1bb90c5..f92febd 100644 (file)
@@ -48,40 +48,39 @@ class external_backpack extends \moodleform {
 
         if (isset($this->_customdata['externalbackpack'])) {
             $backpack = $this->_customdata['externalbackpack'];
-        } else {
-            throw new \coding_exception('backpack is required.');
         }
 
-        $url = $backpack->backpackapiurl;
+        $mform->addElement('hidden', 'action', 'edit');
+        $mform->setType('action', PARAM_ALPHA);
 
-        $mform->addElement('static', 'backpackapiurlinfo', get_string('backpackapiurl', 'core_badges'), $url);
+        if ($backpack) {
+            $mform->addElement('hidden', 'id', $backpack->id);
+            $mform->setType('id', PARAM_INTEGER);
+        }
 
-        $mform->addElement('hidden', 'backpackapiurl', $url);
+        $mform->addElement('text', 'backpackapiurl',  get_string('backpackapiurl', 'core_badges'));
         $mform->setType('backpackapiurl', PARAM_URL);
+        $mform->addRule('backpackapiurl', null, 'required', null, 'client');
+        $mform->addRule('backpackapiurl', get_string('maximumchars', '', 255), 'maxlength', 50, 'client');
 
-        $url = $backpack->backpackweburl;
-        $mform->addElement('static', 'backpackweburlinfo', get_string('backpackweburl', 'core_badges'), $url);
-        $mform->addElement('hidden', 'backpackweburl', $url);
+        $mform->addElement('text', 'backpackweburl', get_string('backpackweburl', 'core_badges'));
         $mform->setType('backpackweburl', PARAM_URL);
+        $mform->addRule('backpackweburl', null, 'required', null, 'client');
+        $mform->addRule('backpackweburl', get_string('maximumchars', '', 255), 'maxlength', 50, 'client');
 
-        $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);
+        $apiversions = badges_get_badge_api_versions();
+        $mform->addElement('select', 'apiversion', get_string('apiversion', 'core_badges'), $apiversions);
         $mform->setType('apiversion', PARAM_RAW);
-
-        $mform->addElement('hidden', 'id', $backpack->id);
-        $mform->setType('id', PARAM_INTEGER);
-
-        $mform->addElement('hidden', 'action', 'edit');
-        $mform->setType('action', PARAM_ALPHA);
+        $mform->setDefault('apiversion', OPEN_BADGES_V2P1);
+        $mform->addRule('apiversion', null, 'required', null, 'client');
 
         $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);
-        if ($backpack->apiversion != OPEN_BADGES_V2P1) {
+
+        if ($backpack && $backpack->apiversion != OPEN_BADGES_V2P1) {
             $mform->addElement('passwordunmask', 'password', get_string('defaultissuerpassword', 'core_badges'));
             $mform->setType('password', PARAM_RAW);
             $mform->addHelpButton('password', 'defaultissuerpassword', 'badges');
@@ -91,7 +90,9 @@ class external_backpack extends \moodleform {
             $mform->addElement('select', 'oauth2_issuerid', get_string('oauth2issuer', 'core_badges'), $oauth2options);
             $mform->setType('oauth2_issuerid', PARAM_INT);
         }
-        $this->set_data($backpack);
+        if ($backpack) {
+            $this->set_data($backpack);
+        }
 
         // Disable short forms.
         $mform->setDisableShortforms();
@@ -99,4 +100,24 @@ class external_backpack extends \moodleform {
         $this->add_action_buttons();
     }
 
+    /**
+     * Validate the data from the form.
+     *
+     * @param  array $data form data
+     * @param  array $files form files
+     * @return array An array of error messages.
+     */
+    public function validation($data, $files) {
+        $errors = parent::validation($data, $files);
+
+        // Ensure backpackapiurl and  are valid URLs.
+        if (!empty($data['backpackapiurl']) && !preg_match('@^https?://.+@', $data['backpackapiurl'])) {
+            $errors['backpackapiurl'] = get_string('invalidurl', 'badges');
+        }
+        if (!empty($data['backpackweburl']) && !preg_match('@^https?://.+@', $data['backpackweburl'])) {
+            $errors['backpackweburl'] = get_string('invalidurl', 'badges');
+        }
+
+        return $errors;
+    }
 }
diff --git a/badges/classes/helper.php b/badges/classes/helper.php
new file mode 100644 (file)
index 0000000..b744c1e
--- /dev/null
@@ -0,0 +1,75 @@
+<?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 helper library.
+ *
+ * @package    core
+ * @subpackage badges
+ * @copyright  2020 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace core_badges;
+
+/**
+ * Badge helper library.
+ *
+ * @copyright  2020 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class helper {
+
+    /**
+     * Create a backpack.
+     *
+     * @param array $params Parameters.
+     * @return object
+     */
+    public static function create_fake_backpack(array $params = []) {
+        global $DB;
+
+        $record = (object) array_merge([
+            'userid' => null,
+            'email' => 'test@example.com',
+            'backpackuid' => -1,
+            'autosync' => 0,
+            'password' => '',
+            'externalbackpackid' => 12345,
+        ], $params);
+        $record->id = $DB->insert_record('badge_backpack', $record);
+
+        return $record;
+    }
+
+    /**
+     * Create a user backpack collection.
+     *
+     * @param array $params Parameters.
+     * @return object
+     */
+    public static function create_fake_backpack_collection(array $params = []) {
+        global $DB;
+
+        $record = (object) array_merge([
+            'backpackid' => 12345,
+            'collectionid' => -1,
+            'entityid' => random_string(20),
+        ], $params);
+        $record->id = $DB->insert_record('badge_external', $record);
+
+        return $record;
+    }
+}
index 839a731..f9258b3 100644 (file)
@@ -56,6 +56,10 @@ class external_backpacks_page implements \renderable {
      * @return stdClass
      */
     public function export_for_template(\renderer_base $output) {
+        global $CFG, $PAGE;
+
+        $PAGE->requires->js_call_amd('core_badges/backpackactions', 'init');
+
         $data = new \stdClass();
         $data->baseurl = $this->url;
         $data->backpacks = array();
@@ -68,6 +72,8 @@ class external_backpacks_page implements \renderable {
             } else {
                 $backpack->canedit = false;
             }
+            $backpack->iscurrent = ($backpack->id == $CFG->badges_site_backpack);
+
             $data->backpacks[] = $backpack;
         }
         $data->warning = badges_verify_site_backpack();
index 2d84733..aab0f2f 100644 (file)
         "warning": "<span class='text-warning'>Could not login</span>"
     }
 }}
-<table class="generaltable fullwidth">
+
+<form action="{{baseurl}}" method="get" id="createbackpack">
+   <input type="hidden" name="action" value="edit"/>
+   <button type="submit" class="btn btn-secondary">{{#str}}newbackpack, core_badges{{/str}}</button>
+</form>
+
+<table class="generaltable fullwidth" id="backpacklist">
     <caption>{{#str}}listbackpacks, core_badges{{/str}}</caption>
     <thead>
         <tr>
     </thead>
     <tbody>
         {{#backpacks}}
-        <tr>
+        <tr data-backpackurl="{{{backpackweburl}}}">
             <td> {{{backpackweburl}}} </td>
             <td> {{#sitebackpack}}Yes{{/sitebackpack}} </td>
             <td>
             {{#canedit}}
-                <a href="{{baseurl}}?id={{id}}&action=edit">
-                    {{#str}}editsettings, core_badges{{/str}}
-                </a>
+                <a href="{{baseurl}}?id={{id}}&action=edit">{{#pix}}t/edit, core,{{#str}}editsettings{{/str}}{{/pix}}</a>
             {{/canedit}}
+            {{^iscurrent}}
+                <a href="{{baseurl}}?id={{id}}&action=delete" role="button" data-action="deletebackpack">
+                    {{#pix}}t/delete, core,{{#str}}delete{{/str}}{{/pix}}
+                </a>
+            {{/iscurrent}}
             </td>
         </tr>
         {{/backpacks}}
index d3d3541..596c67e 100644 (file)
@@ -30,6 +30,8 @@ global $CFG;
 require_once($CFG->libdir . '/badgeslib.php');
 require_once($CFG->dirroot . '/badges/lib.php');
 
+use core_badges\helper;
+
 class core_badges_badgeslib_testcase extends advanced_testcase {
     protected $badgeid;
     protected $course;
@@ -855,4 +857,58 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
         $badge->delete_alignment($alignments1[$newid2]->id);
         $this->assertCount(1, $badge->get_alignments());
     }
+
+    /**
+     * Test badges_delete_site_backpack().
+     *
+     */
+    public function test_badges_delete_site_backpack(): void {
+        global $DB;
+
+        $this->setAdminUser();
+
+        // Create one backpack.
+        $total = $DB->count_records('badge_external_backpack');
+        $this->assertEquals(1, $total);
+
+        $data = new \stdClass();
+        $data->apiversion = OPEN_BADGES_V2P1;
+        $data->backpackapiurl = 'https://dc.imsglobal.org/obchost/ims/ob/v2p1';
+        $data->backpackweburl = 'https://dc.imsglobal.org';
+        badges_create_site_backpack($data);
+        $backpack = $DB->get_record('badge_external_backpack', ['backpackweburl' => $data->backpackweburl]);
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        // User1 is connected to the backpack to be removed and has 2 collections.
+        $backpackuser1 = helper::create_fake_backpack(['userid' => $user1->id, 'externalbackpackid' => $backpack->id]);
+        helper::create_fake_backpack_collection(['backpackid' => $backpackuser1->id]);
+        helper::create_fake_backpack_collection(['backpackid' => $backpackuser1->id]);
+        // User2 is connected to a different backpack and has 1 collection.
+        $backpackuser2 = helper::create_fake_backpack(['userid' => $user2->id]);
+        helper::create_fake_backpack_collection(['backpackid' => $backpackuser2->id]);
+
+        $total = $DB->count_records('badge_external_backpack');
+        $this->assertEquals(2, $total);
+        $total = $DB->count_records('badge_backpack');
+        $this->assertEquals(2, $total);
+        $total = $DB->count_records('badge_external');
+        $this->assertEquals(3, $total);
+
+        // Remove the backpack created previously.
+        $result = badges_delete_site_backpack($backpack->id);
+        $this->assertTrue($result);
+
+        $total = $DB->count_records('badge_external_backpack');
+        $this->assertEquals(1, $total);
+
+        $total = $DB->count_records('badge_backpack');
+        $this->assertEquals(1, $total);
+
+        $total = $DB->count_records('badge_external');
+        $this->assertEquals(1, $total);
+
+        // Try to remove an non-existent backpack.
+        $result = badges_delete_site_backpack($backpack->id);
+        $this->assertFalse($result);
+    }
 }
index 7c66ac7..3cb8e78 100644 (file)
@@ -92,3 +92,30 @@ Feature: Backpack badges
     And I follow "Manage badges"
     And I should see "Test badge verify backpack"
     And "Add to backpack" "link" should exist
+
+  @javascript
+  Scenario: Add a new site backpack
+    Given I am on homepage
+    And I log in as "admin"
+    And I navigate to "Badges > Manage backpacks" in site administration
+    When I press "Add a new backpack"
+    And I set the field "backpackapiurl" to "http://backpackapiurl.cat"
+    And I set the field "backpackweburl" to "aaa"
+    And I press "Save changes"
+    And I should see "Invalid URL"
+    And I set the field "backpackweburl" to "http://backpackweburl.cat"
+    And I press "Save changes"
+    Then I should see "http://backpackweburl.cat"
+    And "Delete" "button" should exist
+
+  @javascript
+  Scenario: Remove a site backpack
+    Given I am on homepage
+    And I log in as "admin"
+    And I navigate to "Badges > Manage backpacks" in site administration
+    When I click on "Delete" "link" in the "https://dc.imsglobal.org" "table_row"
+    And I should see "Delete site backpack 'https://dc.imsglobal.org'?"
+    And I click on "Delete" "button" in the "Delete site backpack" "dialogue"
+    Then I should see "The site backpack has been deleted."
+    And I should not see "https://dc.imsglobal.org"
+    And "Delete" "button" should not exist
index fb75770..8067b4e 100644 (file)
@@ -33,6 +33,7 @@ use core_privacy\local\request\transform;
 use core_privacy\local\request\writer;
 use core_badges\privacy\provider;
 use core_privacy\local\request\approved_userlist;
+use core_badges\helper;
 
 require_once($CFG->libdir . '/badgeslib.php');
 
@@ -142,7 +143,7 @@ class core_badges_privacy_testcase extends provider_testcase {
         $b1 = $this->create_badge();
         $b2 = $this->create_badge(['type' => BADGE_TYPE_COURSE, 'courseid' => $c1->id]);
 
-        $this->create_backpack(['userid' => $u1->id]);
+        helper::create_fake_backpack(['userid' => $u1->id]);
         $this->create_manual_award(['recipientid' => $u2->id, 'badgeid' => $b1->id]);
         $this->create_issued(['badgeid' => $b2->id, 'userid' => $u3->id]);
 
@@ -182,8 +183,8 @@ class core_badges_privacy_testcase extends provider_testcase {
         $b2 = $this->create_badge(['usercreated' => $u2->id, 'usermodified' => $u1->id,
             'type' => BADGE_TYPE_COURSE, 'courseid' => $c1->id]);
 
-        $this->create_backpack(['userid' => $u1->id]);
-        $this->create_backpack(['userid' => $u2->id]);
+        helper::create_fake_backpack(['userid' => $u1->id]);
+        helper::create_fake_backpack(['userid' => $u2->id]);
         $this->create_manual_award(['recipientid' => $u1->id, 'badgeid' => $b1->id]);
         $this->create_manual_award(['recipientid' => $u2->id, 'badgeid' => $b1->id, 'issuerid' => $u1->id]);
         $this->create_issued(['badgeid' => $b2->id, 'userid' => $u1->id]);
@@ -240,8 +241,8 @@ class core_badges_privacy_testcase extends provider_testcase {
         $b2 = $this->create_badge(['usercreated' => $u2->id, 'usermodified' => $u1->id,
             'type' => BADGE_TYPE_COURSE, 'courseid' => $c1->id]);
 
-        $this->create_backpack(['userid' => $u1->id]);
-        $this->create_backpack(['userid' => $u2->id]);
+        helper::create_fake_backpack(['userid' => $u1->id]);
+        helper::create_fake_backpack(['userid' => $u2->id]);
         $this->create_manual_award(['recipientid' => $u1->id, 'badgeid' => $b1->id]);
         $this->create_manual_award(['recipientid' => $u2->id, 'badgeid' => $b1->id, 'issuerid' => $u1->id]);
         $this->create_issued(['badgeid' => $b2->id, 'userid' => $u1->id]);
@@ -317,14 +318,14 @@ class core_badges_privacy_testcase extends provider_testcase {
 
         // Create things for user 2, to check it's not exported it.
         $this->create_issued(['badgeid' => $b4->id, 'userid' => $u2->id]);
-        $this->create_backpack(['userid' => $u2->id, 'email' => $u2->email]);
+        helper::create_fake_backpack(['userid' => $u2->id, 'email' => $u2->email]);
         $this->create_manual_award(['badgeid' => $b1->id, 'recipientid' => $u2->id, 'issuerid' => $u3->id]);
 
         // Create a set of stuff for u1.
         $this->create_issued(['badgeid' => $b1->id, 'userid' => $u1->id, 'uniquehash' => 'yoohoo']);
         $this->create_manual_award(['badgeid' => $b2->id, 'recipientid' => $u1->id, 'issuerid' => $u3->id]);
         $b3crit->mark_complete($u1->id);
-        $this->create_backpack(['userid' => $u1->id, 'email' => $u1->email]);
+        helper::create_fake_backpack(['userid' => $u1->id, 'email' => $u1->email]);
 
         // Check u1.
         writer::reset();
@@ -482,7 +483,7 @@ class core_badges_privacy_testcase extends provider_testcase {
         $this->create_manual_award(['recipientid' => $user3->id, 'issuerid' => $user2->id, 'badgeid' => $badge1->id]);
         $this->create_manual_award(['recipientid' => $user1->id, 'issuerid' => $user2->id, 'badgeid' => $badge2->id]);
 
-        $this->create_backpack(['userid' => $user2->id]);
+        helper::create_fake_backpack(['userid' => $user2->id]);
         $this->create_issued(['badgeid' => $badge2->id, 'userid' => $user3->id]);
 
         $crit = $this->create_criteria_manual($badge1->id);
@@ -538,7 +539,7 @@ class core_badges_privacy_testcase extends provider_testcase {
         $this->create_manual_award(['recipientid' => $user3->id, 'issuerid' => $user2->id, 'badgeid' => $badge1->id]);
         $this->create_manual_award(['recipientid' => $user1->id, 'issuerid' => $user2->id, 'badgeid' => $badge2->id]);
 
-        $this->create_backpack(['userid' => $user2->id]);
+        helper::create_fake_backpack(['userid' => $user2->id]);
         $this->create_issued(['badgeid' => $badge2->id, 'userid' => $user3->id]);
 
         $crit = $this->create_criteria_manual($badge1->id);
@@ -708,26 +709,6 @@ class core_badges_privacy_testcase extends provider_testcase {
         return $record;
     }
 
-    /**
-     * Create a backpack.
-     *
-     * @param array $params Parameters.
-     * @return object
-     */
-    protected function create_backpack(array $params = []) {
-        global $DB;
-        $record = (object) array_merge([
-            'userid' => null,
-            'email' => 'test@example.com',
-            'backpackurl' => "http://here.there.com",
-            'backpackuid' => "12345",
-            'autosync' => 0,
-            'password' => '',
-        ], $params);
-        $record->id = $DB->insert_record('badge_backpack', $record);
-        return $record;
-    }
-
     /**
      * Create a criteria of type badge.
      *
index fa6690d..11bbe84 100644 (file)
@@ -270,6 +270,7 @@ $string['defaultissuerpassword_help'] = 'An account is required on the backpack
 $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['delexternalbackpack'] = 'Delete site backpack';
 $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>
@@ -279,7 +280,6 @@ $string['delparamconfirm'] = 'Are you sure that you want to delete this paramete
 $string['description'] = 'Description';
 $string['disconnect'] = 'Disconnect';
 $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['endorsement'] = 'Endorsement';
 $string['error:backpackdatainvalid'] = 'The data return from the backpack was invalid.';
@@ -402,6 +402,7 @@ $string['month'] = 'Month(s)';
 $string['mybadges'] = 'My badges';
 $string['mybackpack'] = 'My backpack settings';
 $string['never'] = 'Never';
+$string['newbackpack'] = 'Add a new backpack';
 $string['newbadge'] = 'Add a new badge';
 $string['newimage'] = 'New image';
 $string['noalignment'] = 'This badge does not have any external skills or standards specified.';
@@ -513,6 +514,8 @@ $string['selecting'] = 'With selected badges...';
 $string['setup'] = 'Set up connection';
 $string['sitebackpack'] = 'Active external backpack';
 $string['sitebackpack_help'] = 'The external backpack that users can connect to from this site. Note that changing this setting after users have connected their backpacks will require each user to go to their backpack settings page and disconnect then reconnect.';
+$string['sitebackpackdeleted'] = 'The site backpack has been deleted.';
+$string['sitebackpacknotdeleted'] = 'This backpack couldn\'t be deleted because it\'s currently the site default.';
 $string['sitebackpackverify'] = 'Backpack connection';
 $string['sitebackpackwarning'] = 'Could not connect to backpack. <br/><br/>Check that the "Badge issuer email address" admin setting is the valid email for an account on the backpack website. <br/><br/>Check that the "Badge issuer password" on the <a href="{$a->url}">site backpack settings page</a>, is the correct password for the account on the backpack website. <br/><br/>The backpack returned: "{$a->warning}"';
 $string['sitebadges'] = 'Site badges';
@@ -572,3 +575,6 @@ $string['backpackbadges'] = 'You have {$a->totalbadges} badge(s) displayed from
 $string['error:nogroups'] = '<p>There are no public collections of badges available in your backpack. </p> <p>Only public collections are shown. <a href="https://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>.';
+
+// Deprecated since Moodle 3.9.
+$string['editsettings'] = 'Edit settings';
index 280b3bd..0164c28 100644 (file)
@@ -141,3 +141,4 @@ europe/belfast,core_timezones
 pacific/ponape,core_timezones
 pacific/truk,core_timezones
 pacific/yap,core_timezones
+editsettings,core_badges
\ No newline at end of file
index 6790227..c1d2d0f 100644 (file)
@@ -809,6 +809,47 @@ function badges_update_site_backpack($id, $data) {
     return false;
 }
 
+
+/**
+ * Delete the backpack with this id.
+ *
+ * @param integer $id The backpack to delete.
+ * @return boolean
+ */
+function badges_delete_site_backpack($id) {
+    global $DB, $CFG;
+
+    $context = context_system::instance();
+    require_capability('moodle/badges:manageglobalsettings', $context);
+
+    // Only remove site backpack if it's not the default one.
+    if ($CFG->badges_site_backpack != $id && $DB->record_exists('badge_external_backpack', ['id' => $id])) {
+        $transaction = $DB->start_delegated_transaction();
+
+        // Remove connections for users to this backpack.
+        $sql = "SELECT DISTINCT bb.id
+                  FROM {badge_backpack} bb
+                 WHERE bb.externalbackpackid = :backpackid";
+        $params = ['backpackid' => $id];
+        $userbackpacks = $DB->get_fieldset_sql($sql, $params);
+        if ($userbackpacks) {
+            // Delete user external collections references to this backpack.
+            list($insql, $params) = $DB->get_in_or_equal($userbackpacks);
+            $DB->delete_records_select('badge_external', "backpackid $insql", $params);
+        }
+        $DB->delete_records('badge_backpack', ['externalbackpackid' => $id]);
+
+        // Delete backpack entry.
+        $result = $DB->delete_records('badge_external_backpack', ['id' => $id]);
+
+        $transaction->allow_commit();
+
+        return $result;
+    }
+
+    return false;
+}
+
 /**
  * Is any backpack enabled that supports open badges V1?
  * @return boolean