From e8bfd9b4a9ab48b6880a09e3b3651c392d8dde3a Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Mon, 21 Jan 2019 12:10:07 +0800 Subject: [PATCH 1/8] MDL-63876 badges: Remove invalid table from badges --- badges/{competency.php => alignment.php} | 18 ++--- ...etency_action.php => alignment_action.php} | 6 +- ...competency_form.php => alignment_form.php} | 10 +-- badges/badge_json.php | 8 +- badges/classes/assertion.php | 12 +-- badges/classes/external.php | 14 ++-- ...cy_exporter.php => alignment_exporter.php} | 8 +- .../classes/external/user_badge_exporter.php | 22 +++--- badges/renderer.php | 64 +++++++-------- badges/tests/badgeslib_test.php | 78 +++++++++---------- badges/tests/behat/add_badge.feature | 21 +++-- badges/tests/external_test.php | 56 ++++++------- badges/upgrade.txt | 2 + lib/badgeslib.php | 30 +++---- lib/db/install.xml | 4 +- lib/db/upgrade.php | 24 ++++++ lib/setuplib.php | 2 +- version.php | 2 +- 18 files changed, 202 insertions(+), 179 deletions(-) rename badges/{competency.php => alignment.php} (83%) rename badges/{competency_action.php => alignment_action.php} (90%) rename badges/{competency_form.php => alignment_form.php} (94%) rename badges/classes/external/{competency_exporter.php => alignment_exporter.php} (92%) diff --git a/badges/competency.php b/badges/alignment.php similarity index 83% rename from badges/competency.php rename to badges/alignment.php index 1eae429f2d2..f67a3045b92 100644 --- a/badges/competency.php +++ b/badges/alignment.php @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . /** - * List competencies, skills, or standards are targeted by a BadgeClass. + * List alignments, skills, or standards are targeted by a BadgeClass. * * @package core * @subpackage badges @@ -24,7 +24,7 @@ */ require_once(__DIR__ . '/../config.php'); require_once($CFG->libdir . '/badgeslib.php'); -require_once($CFG->dirroot . '/badges/competency_form.php'); +require_once($CFG->dirroot . '/badges/alignment_form.php'); $badgeid = required_param('id', PARAM_INT); $alignmentid = optional_param('alignmentid', 0, PARAM_INT); @@ -53,7 +53,7 @@ if ($badge->type == BADGE_TYPE_COURSE) { navigation_node::override_active_url($navurl, true); } -$currenturl = new moodle_url('/badges/competency.php', array('id' => $badge->id)); +$currenturl = new moodle_url('/badges/alignment.php', array('id' => $badge->id)); $PAGE->set_context($context); $PAGE->set_url($currenturl); $PAGE->set_heading($badge->name); @@ -63,8 +63,8 @@ $PAGE->navbar->add($badge->name); $output = $PAGE->get_renderer('core', 'badges'); $msg = optional_param('msg', '', PARAM_TEXT); $emsg = optional_param('emsg', '', PARAM_TEXT); -$url = new moodle_url('/badges/competency.php', array('id' => $badge->id, 'action' => $action, 'alignmentid' => $alignmentid)); -$mform = new competency_alignment_form($url, array('badge' => $badge, 'action' => $action, 'alignmentid' => $alignmentid)); +$url = new moodle_url('/badges/alignment.php', array('id' => $badge->id, 'action' => $action, 'alignmentid' => $alignmentid)); +$mform = new alignment_form($url, array('badge' => $badge, 'action' => $action, 'alignmentid' => $alignmentid)); if ($mform->is_cancelled()) { redirect($currenturl); } else if ($mform->is_submitted() && $mform->is_validated() && ($data = $mform->get_data())) { @@ -94,12 +94,12 @@ if ($alignmentid || $action == 'add' || $action == 'edit') { $mform->display(); } else if (empty($action)) { if (!$badge->is_active() && !$badge->is_locked()) { - $urlbutton = new moodle_url('/badges/competency.php', array('id' => $badge->id, 'action' => 'add')); + $urlbutton = new moodle_url('/badges/alignment.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(); - if (count($alignment) > 0) { - $renderrelated = new badge_competencies_alignment($alignment, $badgeid); + $alignments = $badge->get_alignments(); + if (count($alignments) > 0) { + $renderrelated = new badge_alignments($alignments, $badgeid); echo $output->render($renderrelated); } else { echo $output->notification(get_string('noalignment', 'badges')); diff --git a/badges/competency_action.php b/badges/alignment_action.php similarity index 90% rename from badges/competency_action.php rename to badges/alignment_action.php index f56da92fbe5..a3815469f3d 100644 --- a/badges/competency_action.php +++ b/badges/alignment_action.php @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . /** - * Processing actions with competencies alignment. + * Processing actions with alignments. * * @package core * @subpackage badges @@ -30,7 +30,7 @@ $badgeid = required_param('id', PARAM_INT); // Badge ID. $action = optional_param('action', 'remove', PARAM_TEXT); // Remove. require_login(); -$return = new moodle_url('/badges/competency.php', array('id' => $badgeid)); +$return = new moodle_url('/badges/alignment.php', array('id' => $badgeid)); $badge = new badge($badgeid); $context = $badge->get_context(); require_capability('moodle/badges:configuredetails', $context); @@ -38,4 +38,4 @@ require_capability('moodle/badges:configuredetails', $context); if ($action == 'remove') { $badge->delete_alignment($alignmentid); } -redirect($return); \ No newline at end of file +redirect($return); diff --git a/badges/competency_form.php b/badges/alignment_form.php similarity index 94% rename from badges/competency_form.php rename to badges/alignment_form.php index 64557fa39b1..9de0bf77fc1 100644 --- a/badges/competency_form.php +++ b/badges/alignment_form.php @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . /** - * Form competency for editing. + * Form alignment for editing. * * @package core * @subpackage badges @@ -27,13 +27,13 @@ require_once($CFG->libdir . '/formslib.php'); require_once($CFG->libdir . '/badgeslib.php'); /** - * Form to edit competency alignment. + * Form to edit alignment. * * @copyright 2018 Tung Thai * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @author Tung Thai */ -class competency_alignment_form extends moodleform { +class alignment_form extends moodleform { /** * Defines the form. @@ -68,7 +68,7 @@ class competency_alignment_form extends moodleform { $this->add_action_buttons(); if ($action == 'edit' || $alignmentid) { $alignment = new stdClass(); - $alignment = $DB->get_record_select('badge_competencies', 'id = ?', array($alignmentid)); + $alignment = $DB->get_record_select('badge_alignment', 'id = ?', array($alignmentid)); $this->set_data($alignment); // Freeze all elements if badge is active or locked. if ($badge->is_active() || $badge->is_locked()) { @@ -91,4 +91,4 @@ class competency_alignment_form extends moodleform { } return $errors; } -} \ No newline at end of file +} diff --git a/badges/badge_json.php b/badges/badge_json.php index 907f0f10dc9..2362bade70d 100644 --- a/badges/badge_json.php +++ b/badges/badge_json.php @@ -95,9 +95,9 @@ if ($badge->status != BADGE_STATUS_INACTIVE) { $json['endorsement'] = $endorsementurl->out(false); } - $competencies = $badge->get_alignment(); - if (!empty($competencies)) { - foreach ($competencies as $item) { + $alignments = $badge->get_alignments(); + if (!empty($alignments)) { + foreach ($alignments as $item) { $alignment = array('targetName' => $item->targetname, 'targetUrl' => $item->targeturl); if ($item->targetdescription) { $alignment['targetDescription'] = $item->targetdescription; @@ -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/assertion.php b/badges/classes/assertion.php index 417594d5243..e1e0451906d 100644 --- a/badges/classes/assertion.php +++ b/badges/classes/assertion.php @@ -223,15 +223,15 @@ class core_badges_assertion { } /** - * Get competencies alignment of the badge. + * Get alignment of the badge. * - * @return array competencies information. + * @return array information. */ - public function get_competencies_alignment() { + public function get_alignments() { global $DB; $badgeid = $this->_data->id; $alignments = array(); - $items = $DB->get_records_select('badge_competencies', 'badgeid = ?', array($badgeid)); + $items = $DB->get_records_select('badge_alignment', 'badgeid = ?', array($badgeid)); foreach ($items as $item) { $alignment = array('targetName' => $item->targetname, 'targetUrl' => $item->targeturl); if ($item->targetdescription) { @@ -301,8 +301,8 @@ class core_badges_assertion { $endorsementurl = new moodle_url('/badges/endorsement_json.php', array('id' => $this->_data->id)); $json['endorsement'] = $endorsementurl->out(false); } - if ($competencies = $this->get_competencies_alignment()) { - $json['alignment'] = $competencies; + if ($alignments = $this->get_alignments()) { + $json['alignment'] = $alignments; } if ($this->_data->imageauthorname || $this->_data->imageauthoremail || diff --git a/badges/classes/external.php b/badges/classes/external.php index 15437d09c2f..8ef4b6ecb7f 100644 --- a/badges/classes/external.php +++ b/badges/classes/external.php @@ -150,17 +150,17 @@ 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(); + $alignments = $badgeinstance->get_alignments(); $relatedbadges = $badgeinstance->get_related_badges(); if (!$canconfiguredetails) { // Return only the properties visible by the user. - if (!empty($competencies)) { - foreach ($competencies as $competency) { - unset($competency->targetdescription); - unset($competency->targetframework); - unset($competency->targetcode); + if (!empty($alignments)) { + foreach ($alignments as $alignment) { + unset($alignment->targetdescription); + unset($alignment->targetframework); + unset($alignment->targetcode); } } @@ -176,7 +176,7 @@ class core_badges_external extends external_api { $related = array( 'context' => $context, 'endorsement' => $endorsement ? $endorsement : null, - 'competencies' => $competencies, + 'alignments' => $alignments, 'relatedbadges' => $relatedbadges, ); diff --git a/badges/classes/external/competency_exporter.php b/badges/classes/external/alignment_exporter.php similarity index 92% rename from badges/classes/external/competency_exporter.php rename to badges/classes/external/alignment_exporter.php index 3eca46a2027..91a431c2527 100644 --- a/badges/classes/external/competency_exporter.php +++ b/badges/classes/external/alignment_exporter.php @@ -15,7 +15,7 @@ // along with Moodle. If not, see . /** - * Contains competency class for displaying a badge competency. + * Contains alignment class for displaying a badge alignment. * * @package core_badges * @copyright 2018 Dani Palou @@ -29,13 +29,13 @@ defined('MOODLE_INTERNAL') || die(); use core\external\exporter; /** - * Class for displaying a badge competency. + * Class for displaying a badge alignment. * * @package core_badges * @copyright 2018 Dani Palou * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class competency_exporter extends exporter { +class alignment_exporter extends exporter { /** * Return the list of properties. @@ -46,7 +46,7 @@ class competency_exporter extends exporter { return [ 'id' => [ 'type' => PARAM_INT, - 'description' => 'Competency id', + 'description' => 'Alignment id', ], 'badgeid' => [ 'type' => PARAM_INT, diff --git a/badges/classes/external/user_badge_exporter.php b/badges/classes/external/user_badge_exporter.php index 0cc7269da3e..cf07bd6c46b 100644 --- a/badges/classes/external/user_badge_exporter.php +++ b/badges/classes/external/user_badge_exporter.php @@ -30,7 +30,7 @@ use core\external\exporter; use renderer_base; use moodle_url; use core_badges\external\endorsement_exporter; -use core_badges\external\competency_exporter; +use core_badges\external\alignment_exporter; use core_badges\external\related_info_exporter; /** @@ -234,7 +234,7 @@ class user_badge_exporter extends exporter { return array( 'context' => 'context', 'endorsement' => 'stdClass?', - 'competencies' => 'stdClass[]', + 'alignments' => 'stdClass[]', 'relatedbadges' => 'stdClass[]', ); } @@ -255,9 +255,9 @@ class user_badge_exporter extends exporter { 'description' => 'Badge endorsement', 'optional' => true, ], - 'competencies' => [ - 'type' => competency_exporter::read_properties_definition(), - 'description' => 'Badge competencies (alignment)', + 'alignments' => [ + 'type' => alignment_exporter::read_properties_definition(), + 'description' => 'Badge alignments', 'multiple' => true, ], 'relatedbadges' => [ @@ -277,13 +277,13 @@ class user_badge_exporter extends exporter { protected function get_other_values(renderer_base $output) { $context = $this->related['context']; $endorsement = $this->related['endorsement']; - $competencies = $this->related['competencies']; + $alignments = $this->related['alignments']; $relatedbadges = $this->related['relatedbadges']; $values = array( 'badgeurl' => moodle_url::make_webservice_pluginfile_url($context->id, 'badges', 'badgeimage', $this->data->id, '/', 'f1')->out(false), - 'competencies' => array(), + 'alignments' => array(), 'relatedbadges' => array(), ); @@ -292,10 +292,10 @@ class user_badge_exporter extends exporter { $values['endorsement'] = $endorsementexporter->export($output); } - if (!empty($competencies)) { - foreach ($competencies as $competency) { - $competencyexporter = new competency_exporter($competency, array('context' => $context)); - $values['competencies'][] = $competencyexporter->export($output); + if (!empty($alignments)) { + foreach ($alignments as $alignment) { + $alignmentexporter = new alignment_exporter($alignment, array('context' => $context)); + $values['alignments'][] = $alignmentexporter->export($output); } } diff --git a/badges/renderer.php b/badges/renderer.php index 98d846666fe..a401c950cc3 100644 --- a/badges/renderer.php +++ b/badges/renderer.php @@ -222,7 +222,7 @@ class core_badges_renderer extends plugin_renderer_base { $display .= self::print_badge_endorsement($badge); $display .= self::print_badge_related($badge); - $display .= self::print_badge_competencies($badge); + $display .= self::print_badge_alignments($badge); return html_writer::div($display, null, array('id' => 'badge-overview')); } @@ -431,12 +431,12 @@ class core_badges_renderer extends plugin_renderer_base { $output .= html_writer::alist($items, array(), 'ul'); } - $competencies = $badge->get_alignment(); - if (!empty($competencies)) { + $alignments = $badge->get_alignments(); + if (!empty($alignments)) { $output .= $this->heading(get_string('alignment', 'badges'), 3); $items = array(); - foreach ($competencies as $competency) { - $items[] = html_writer::link($competency->targeturl, $competency->targetname, array('target' => '_blank')); + foreach ($alignments as $alignment) { + $items[] = html_writer::link($alignment->targeturl, $alignment->targetname, array('target' => '_blank')); } $output .= html_writer::alist($items, array(), 'ul'); } @@ -748,11 +748,11 @@ class core_badges_renderer extends plugin_renderer_base { } if (has_capability('moodle/badges:configuredetails', $context)) { - $competencies = $DB->count_records_sql("SELECT COUNT(bc.id) - FROM {badge_competencies} bc WHERE bc.badgeid = :badgeid", array('badgeid' => $badgeid)); + $alignments = $DB->count_records_sql("SELECT COUNT(bc.id) + FROM {badge_alignment} bc WHERE bc.badgeid = :badgeid", array('badgeid' => $badgeid)); $row[] = new tabobject('balignment', - new moodle_url('/badges/competency.php', array('id' => $badgeid)), - get_string('balignment', 'badges', $competencies) + new moodle_url('/badges/alignment.php', array('id' => $badgeid)), + get_string('balignment', 'badges', $alignments) ); } @@ -1101,22 +1101,22 @@ class core_badges_renderer extends plugin_renderer_base { } /** - * Print list badge competencies. + * Print list badge alignments. * * @param badge $badge Badge objects. - * @return string $output List competencies to output. + * @return string $output List alignments to output. */ - protected function print_badge_competencies(badge $badge) { + protected function print_badge_alignments(badge $badge) { $output = ''; $output .= $this->heading(get_string('alignment', 'badges'), 3); - $competencies = $badge->get_alignment(); - if (!empty($competencies)) { + $alignments = $badge->get_alignments(); + if (!empty($alignments)) { $items = array(); - foreach ($competencies as $competency) { - $urlaligment = new moodle_url('competency.php', - array('id' => $badge->id, 'alignmentid' => $competency->id) + foreach ($alignments as $alignment) { + $urlaligment = new moodle_url('alignment.php', + array('id' => $badge->id, 'alignmentid' => $alignment->id) ); - $items[] = html_writer::link($urlaligment, $competency->targetname, array('target' => '_blank')); + $items[] = html_writer::link($urlaligment, $alignment->targetname, array('target' => '_blank')); } $output .= html_writer::alist($items, array(), 'ul'); } else { @@ -1186,21 +1186,21 @@ class core_badges_renderer extends plugin_renderer_base { } /** - * Renders a table with competencies alignment. + * Renders a table with alignment. * - * @param badge_competencies_alignment $alignment List competencies alignment. - * @return string List competencies aligment to output. + * @param badge_alignments $alignments List alignments. + * @return string List alignment to output. */ - protected function render_badge_competencies_alignment(badge_competencies_alignment $alignment) { - $currentbadge = new badge($alignment->currentbadgeid); - $paging = new paging_bar($alignment->totalcount, $alignment->page, $alignment->perpage, $this->page->url, 'page'); + protected function render_badge_alignments(badge_alignments $alignments) { + $currentbadge = new badge($alignments->currentbadgeid); + $paging = new paging_bar($alignments->totalcount, $alignments->page, $alignments->perpage, $this->page->url, 'page'); $htmlpagingbar = $this->render($paging); $table = new html_table(); $table->attributes['class'] = 'generaltable boxaligncenter boxwidthwide'; $table->head = array('Name', 'URL', ''); - foreach ($alignment->alignments as $item) { - $urlaligment = new moodle_url('competency.php', + foreach ($alignments->alignments as $item) { + $urlaligment = new moodle_url('alignment.php', array( 'id' => $currentbadge->id, 'alignmentid' => $item->id, @@ -1212,7 +1212,7 @@ class core_badges_renderer extends plugin_renderer_base { ); if (!$currentbadge->is_active() && !$currentbadge->is_locked()) { $delete = $this->output->action_icon( - new moodle_url('competency_action.php', + new moodle_url('alignment_action.php', array( 'id' => $currentbadge->id, 'alignmentid' => $item->id, @@ -1220,7 +1220,7 @@ class core_badges_renderer extends plugin_renderer_base { ) ), new pix_icon('t/delete', get_string('delete'))); $edit = $this->output->action_icon( - new moodle_url('competency.php', + new moodle_url('alignment.php', array( 'id' => $currentbadge->id, 'alignmentid' => $item->id, @@ -1480,13 +1480,13 @@ class badge_related implements renderable { } /** - * Collection of all competencies. + * Collection of all alignments. * * @copyright 2018 Tung Thai * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @author Tung Thai */ -class badge_competencies_alignment implements renderable +class badge_alignments implements renderable { /** @var string how are the data sorted. */ public $sort = 'name'; @@ -1510,9 +1510,9 @@ class badge_competencies_alignment implements renderable public $currentbadgeid = 0; /** - * Initializes the list of competencies to display. + * Initializes the list of alignments to display. * - * @param array $alignments List competencies alignment to render. + * @param array $alignments List alignments to render. * @param int $currentbadgeid ID current badge. */ public function __construct($alignments, $currentbadgeid) { diff --git a/badges/tests/badgeslib_test.php b/badges/tests/badgeslib_test.php index d577c787155..d37e417c28c 100644 --- a/badges/tests/badgeslib_test.php +++ b/badges/tests/badgeslib_test.php @@ -119,15 +119,15 @@ class core_badges_badgeslib_testcase extends advanced_testcase { $relatebadge->relatedbadgeid = $clonedid; $relatebadge->relatedid = $DB->insert_record('badge_related', $relatebadge, true); - // Insert a competency aligment. - $competency = new stdClass(); - $competency->badgeid = $this->coursebadge; - $competency->targetname = 'CCSS.ELA-Literacy.RST.11-12.3'; - $competency->targeturl = 'http://www.corestandards.org/ELA-Literacy/RST/11-12/3'; - $competency->targetdescription = 'Test target description'; - $competency->targetframework = 'CCSS.RST.11-12.3'; - $competency->targetcode = 'CCSS.RST.11-12.3'; - $DB->insert_record('badge_competencies', $competency, true); + // Insert a aligment. + $alignment = new stdClass(); + $alignment->badgeid = $this->coursebadge; + $alignment->targetname = 'CCSS.ELA-Literacy.RST.11-12.3'; + $alignment->targeturl = 'http://www.corestandards.org/ELA-Literacy/RST/11-12/3'; + $alignment->targetdescription = 'Test target description'; + $alignment->targetframework = 'CCSS.RST.11-12.3'; + $alignment->targetcode = 'CCSS.RST.11-12.3'; + $DB->insert_record('badge_alignment', $alignment, true); $this->assertion = new stdClass(); $this->assertion->badge = '{"uid":"%s","recipient":{"identity":"%s","type":"email","hashed":true,"salt":"%s"},"badge":"%s","verify":{"type":"hosted","url":"%s"},"issuedOn":"%d","evidence":"%s"}'; @@ -797,41 +797,41 @@ class core_badges_badgeslib_testcase extends advanced_testcase { } /** - * Test insert, update, delete competencies alignment with a site badge. + * Test insert, update, delete alignment with a site badge. */ - public function test_competencies_alignment() { + public function test_alignments() { $badge = new badge($this->badgeid); - // Insert a competency alignment. - $competency1 = new stdClass(); - $competency1->badgeid = $this->badgeid; - $competency1->targetname = 'CCSS.ELA-Literacy.RST.11-12.3'; - $competency1->targeturl = 'http://www.corestandards.org/ELA-Literacy/RST/11-12/3'; - $competency1->targetdescription = 'Test target description'; - $competency1->targetframework = 'CCSS.RST.11-12.3'; - $competency1->targetcode = 'CCSS.RST.11-12.3'; - $competency2 = clone $competency1; - $newid1 = $badge->save_alignment($competency1); - $newid2 = $badge->save_alignment($competency2); - $competencies1 = $badge->get_alignment(); - $this->assertCount(2, $competencies1); + // Insert a alignment. + $alignment1 = new stdClass(); + $alignment1->badgeid = $this->badgeid; + $alignment1->targetname = 'CCSS.ELA-Literacy.RST.11-12.3'; + $alignment1->targeturl = 'http://www.corestandards.org/ELA-Literacy/RST/11-12/3'; + $alignment1->targetdescription = 'Test target description'; + $alignment1->targetframework = 'CCSS.RST.11-12.3'; + $alignment1->targetcode = 'CCSS.RST.11-12.3'; + $alignment2 = clone $alignment1; + $newid1 = $badge->save_alignment($alignment1); + $newid2 = $badge->save_alignment($alignment2); + $alignments1 = $badge->get_alignments(); + $this->assertCount(2, $alignments1); - $this->assertEquals($competency1->badgeid, $competencies1[$newid1]->badgeid); - $this->assertEquals($competency1->targetname, $competencies1[$newid1]->targetname); - $this->assertEquals($competency1->targeturl, $competencies1[$newid1]->targeturl); - $this->assertEquals($competency1->targetdescription, $competencies1[$newid1]->targetdescription); - $this->assertEquals($competency1->targetframework, $competencies1[$newid1]->targetframework); - $this->assertEquals($competency1->targetcode, $competencies1[$newid1]->targetcode); + $this->assertEquals($alignment1->badgeid, $alignments1[$newid1]->badgeid); + $this->assertEquals($alignment1->targetname, $alignments1[$newid1]->targetname); + $this->assertEquals($alignment1->targeturl, $alignments1[$newid1]->targeturl); + $this->assertEquals($alignment1->targetdescription, $alignments1[$newid1]->targetdescription); + $this->assertEquals($alignment1->targetframework, $alignments1[$newid1]->targetframework); + $this->assertEquals($alignment1->targetcode, $alignments1[$newid1]->targetcode); - // 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(); - $this->assertEquals($competencies1[$newid1]->id, $competencies2[$newid1]->id); - $this->assertEquals($competencies1[$newid1]->targetname, $competencies2[$newid1]->targetname); + // Update aligment. + $alignments1[$newid1]->targetname = 'CCSS.ELA-Literacy.RST.11-12.3 update'; + $badge->save_alignment($alignments1[$newid1], $alignments1[$newid1]->id); + $alignments2 = $badge->get_alignments(); + $this->assertEquals($alignments1[$newid1]->id, $alignments2[$newid1]->id); + $this->assertEquals($alignments1[$newid1]->targetname, $alignments2[$newid1]->targetname); - // Delete competency alignment. - $badge->delete_alignment($competencies1[$newid2]->id); - $this->assertCount(1, $badge->get_alignment()); + // Delete alignment. + $badge->delete_alignment($alignments1[$newid2]->id); + $this->assertCount(1, $badge->get_alignments()); } } diff --git a/badges/tests/behat/add_badge.feature b/badges/tests/behat/add_badge.feature index 7c03084f64c..235fdd0bfd3 100644 --- a/badges/tests/behat/add_badge.feature +++ b/badges/tests/behat/add_badge.feature @@ -46,7 +46,7 @@ Feature: Add badges to the system And I should see "Test badge with 'apostrophe' and other friends (&@#)" And I should see "Endorsement" And I should see "Related badges (0)" - And I should see "Competencies (0)" + And I should see "External alignments (0)" And I should not see "Create badge" And I follow "Manage badges" And I should see "Number of badges available: 1" @@ -118,7 +118,7 @@ Feature: Add badges to the system Then I should see "Changes saved" @javascript @_file_upload - Scenario: Competencies alignment for Badge + Scenario: External alignments for Badge Given I navigate to "Badges > Add a new badge" in site administration And I set the following fields to these values: | Name | Test Badge | @@ -133,18 +133,15 @@ Feature: Add badges to the system When I press "Create badge" Then I should see "Test Badge" And I should see "Endorsement" - And I follow "Competencies (0)" - And I should see "This badge does not have any competencies specified." - And I press "Add competency" - And I follow "Competency" - And I wait until the page is ready - And I follow "Competency" + And I follow "External alignments (0)" + And I should see "This badge does not have any external alignments specified." + And I press "Add external alignment" And I set the following fields to these values: - | Competency name | Test Badge Competencies | - | URL | https://competencies.example.com | - | Description | Test Badge Competencies description | + | External alignment name | Test Badge Alignments | + | URL | https://alignments.example.com | + | Description | Test Badge Alignments description | When I press "Save changes" - And I should see "Competencies (1)" + And I should see "External alignments (1)" @javascript @_file_upload Scenario: Add a badge from Site badges section diff --git a/badges/tests/external_test.php b/badges/tests/external_test.php index 9ebf174980a..7bbd982d485 100644 --- a/badges/tests/external_test.php +++ b/badges/tests/external_test.php @@ -110,22 +110,22 @@ class core_badges_external_testcase extends externallib_advanced_testcase { $endorsement->dateissued = $now; $badge->save_endorsement($endorsement); - // Add 2 competencies. - $competency = new stdClass(); - $competency->badgeid = $badgeid; - $competency->targetname = 'Competency 1'; - $competency->targeturl = 'http://c1-target-url.domain.co.nz'; - $competency->targetdescription = 'C1 target description'; - $competency->targetframework = 'C1 framework'; - $competency->targetcode = 'C1 code'; - $badge->save_alignment($competency); + // Add 2 alignments. + $alignment = new stdClass(); + $alignment->badgeid = $badgeid; + $alignment->targetname = 'Alignment 1'; + $alignment->targeturl = 'http://a1-target-url.domain.co.nz'; + $alignment->targetdescription = 'A1 target description'; + $alignment->targetframework = 'A1 framework'; + $alignment->targetcode = 'A1 code'; + $badge->save_alignment($alignment); - $competency->targetname = 'Competency 2'; - $competency->targeturl = 'http://c2-target-url.domain.co.nz'; - $competency->targetdescription = 'C2 target description'; - $competency->targetframework = 'C2 framework'; - $competency->targetcode = 'C2 code'; - $badge->save_alignment($competency); + $alignment->targetname = 'Alignment 2'; + $alignment->targeturl = 'http://a2-target-url.domain.co.nz'; + $alignment->targetdescription = 'A2 target description'; + $alignment->targetframework = 'A2 framework'; + $alignment->targetcode = 'A2 code'; + $badge->save_alignment($alignment); // Now a course badge. $badge->id = null; @@ -162,26 +162,26 @@ class core_badges_external_testcase extends externallib_advanced_testcase { $badge->badgeurl = moodle_url::make_webservice_pluginfile_url($context->id, 'badges', 'badgeimage', $badge->id, '/', 'f1')->out(false); - // Get the endorsement, competencies and related badges. + // Get the endorsement, alignments and related badges. $badgeinstance = new badge($badge->id); $endorsement = $badgeinstance->get_endorsement(); - $competencies = $badgeinstance->get_alignment(); + $alignments = $badgeinstance->get_alignments(); $relatedbadges = $badgeinstance->get_related_badges(); - $badge->competencies = array(); + $badge->alignments = array(); $badge->relatedbadges = array(); if ($endorsement) { $badge->endorsement = (array) $endorsement; } - if (!empty($competencies)) { - foreach ($competencies as $competency) { - // Students cannot see some fields of the competencies. - unset($competency->targetdescription); - unset($competency->targetframework); - unset($competency->targetcode); + if (!empty($alignments)) { + foreach ($alignments as $alignment) { + // Students cannot see some fields of the alignments. + unset($alignment->targetdescription); + unset($alignment->targetframework); + unset($alignment->targetcode); - $badge->competencies[] = (array) $competency; + $badge->alignments[] = (array) $alignment; } } @@ -231,9 +231,9 @@ class core_badges_external_testcase extends externallib_advanced_testcase { if (isset($badge['type']) and $badge['type'] == BADGE_TYPE_COURSE) { $this->assertTrue(isset($badge['message'])); - // Check that we have permissions to see all the data in competencies and related badges. - foreach ($badge['competencies'] as $competency) { - $this->assertTrue(isset($competency['targetdescription'])); + // Check that we have permissions to see all the data in alignments and related badges. + foreach ($badge['alignments'] as $alignment) { + $this->assertTrue(isset($alignment['targetdescription'])); } foreach ($badge['relatedbadges'] as $relatedbadge) { diff --git a/badges/upgrade.txt b/badges/upgrade.txt index 8b2039407c3..407fcdaf081 100644 --- a/badges/upgrade.txt +++ b/badges/upgrade.txt @@ -1,5 +1,7 @@ This files describes API changes in /badges/*, information provided here is intended especially for developers. +=== 3.7 === +* Incorrect term "badge competencies" has been refactored to "alignments" everywhere. === 3.6 === diff --git a/lib/badgeslib.php b/lib/badgeslib.php index d0c1931c428..98c1b9e3746 100644 --- a/lib/badgeslib.php +++ b/lib/badgeslib.php @@ -714,7 +714,7 @@ class badge { $badgecontext = $this->get_context(); $fs->delete_area_files($badgecontext->id, 'badges', 'badgeimage', $this->id); - // Delete endorsements, competencies and related badges. + // Delete endorsements, alignments and related badges. $DB->delete_records('badge_endorsement', array('badgeid' => $this->id)); $relatedsql = 'badgeid = :badgeid OR relatedbadgeid = :relatedbadgeid'; $relatedparams = array( @@ -722,7 +722,7 @@ class badge { 'relatedbadgeid' => $this->id ); $DB->delete_records_select('badge_related', $relatedsql, $relatedparams); - $DB->delete_records('badge_competencies', array('badgeid' => $this->id)); + $DB->delete_records('badge_alignment', array('badgeid' => $this->id)); // Finally, remove badge itself. $DB->delete_records('badge', array('id' => $this->id)); @@ -804,43 +804,43 @@ class badge { } /** - * Insert/update competency alignment information of badge. + * Insert/update alignment information of badge. * - * @param stdClass $alignment Data of a competency alignment. - * @param int $alignmentid ID competency alignment. + * @param stdClass $alignment Data of a alignment. + * @param int $alignmentid ID alignment. * @return bool|int A status/ID when insert or update data. */ public function save_alignment($alignment, $alignmentid = 0) { global $DB; - $record = $DB->record_exists('badge_competencies', array('id' => $alignmentid)); + $record = $DB->record_exists('badge_alignment', array('id' => $alignmentid)); if ($record) { $alignment->id = $alignmentid; - return $DB->update_record('badge_competencies', $alignment); + return $DB->update_record('badge_alignment', $alignment); } else { - return $DB->insert_record('badge_competencies', $alignment, true); + return $DB->insert_record('badge_alignment', $alignment, true); } } /** - * Delete a competency alignment of badge. + * Delete a alignment of badge. * - * @param int $alignmentid ID competency alignment. - * @return bool A status for delete a competency alignment. + * @param int $alignmentid ID alignment. + * @return bool A status for delete a alignment. */ public function delete_alignment($alignmentid) { global $DB; - return $DB->delete_records('badge_competencies', array('badgeid' => $this->id, 'id' => $alignmentid)); + return $DB->delete_records('badge_alignment', array('badgeid' => $this->id, 'id' => $alignmentid)); } /** - * Get competencies of badge. + * Get alignments of badge. * - * @return array List content competencies. + * @return array List content alignments. */ public function get_alignment() { global $DB; - return $DB->get_records('badge_competencies', array('badgeid' => $this->id)); + return $DB->get_records('badge_alignment', array('badgeid' => $this->id)); } /** diff --git a/lib/db/install.xml b/lib/db/install.xml index fd1548f8797..9c127a4edce 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -3200,7 +3200,7 @@ - +
@@ -3212,7 +3212,7 @@ - +
diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 7d28a631551..0b0e59dacd2 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2923,5 +2923,29 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2019032200.02); } + if ($oldversion < 2019032900.00) { + + // Define table badge_competencies to be renamed to badge_alignment. + $table = new xmldb_table('badge_competencies'); + + // Be careful if this step gets run twice. + if ($dbman->table_exists($table)) { + $key = new xmldb_key('competenciesbadge', XMLDB_KEY_FOREIGN, ['badgeid'], 'badge', ['id']); + + // Launch drop key competenciesbadge. + $dbman->drop_key($table, $key); + + $key = new xmldb_key('alignmentsbadge', XMLDB_KEY_FOREIGN, ['badgeid'], 'badge', ['id']); + + // Launch add key alignmentsbadge. + $dbman->add_key($table, $key); + + // Launch rename table for badge_alignment. + $dbman->rename_table($table, 'badge_alignment'); + } + + upgrade_main_savepoint(true, 2019032900.00); + } + return true; } diff --git a/lib/setuplib.php b/lib/setuplib.php index 76a4fea3ead..98a8666ada1 100644 --- a/lib/setuplib.php +++ b/lib/setuplib.php @@ -1407,7 +1407,7 @@ function disable_output_buffering() { */ function is_major_upgrade_required() { global $CFG; - $lastmajordbchanges = 2019011801.00; + $lastmajordbchanges = 2019032900.00; $required = empty($CFG->version); $required = $required || (float)$CFG->version < $lastmajordbchanges; diff --git a/version.php b/version.php index eeaaa852057..eb1f55bf886 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2019032800.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2019032900.00; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. From f94786e0e19729e1357308c6036555e497caddf7 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Thu, 17 Jan 2019 11:35:57 +0800 Subject: [PATCH 2/8] MDL-63876 badges: Add competency criteria Properly integration badges with competencies provided in Moodle. Automatically grant the badge when the defined competencies are marked as proficient. --- admin/tool/lp/classes/external.php | 2 +- admin/tool/lp/competencies.php | 2 +- admin/tool/lp/styles.css | 14 +- badges/amd/build/competency.min.js | 1 + badges/amd/src/competency.js | 136 ++++++++++ badges/classes/observer.php | 42 ++++ badges/criteria/award_criteria.php | 25 +- badges/criteria/award_criteria_competency.php | 237 ++++++++++++++++++ badges/criteria_form.php | 2 +- .../award_criteria_competencies.mustache | 70 ++++++ badges/tests/behat/add_badge.feature | 16 +- .../tests/behat/criteria_competency.feature | 119 +++++++++ lang/en/badges.php | 33 ++- lib/badgeslib.php | 7 +- lib/db/events.php | 5 +- 15 files changed, 674 insertions(+), 37 deletions(-) create mode 100644 badges/amd/build/competency.min.js create mode 100644 badges/amd/src/competency.js create mode 100644 badges/criteria/award_criteria_competency.php create mode 100644 badges/templates/award_criteria_competencies.mustache create mode 100644 badges/tests/behat/criteria_competency.feature diff --git a/admin/tool/lp/classes/external.php b/admin/tool/lp/classes/external.php index f798e7d5025..d79317c1dcd 100644 --- a/admin/tool/lp/classes/external.php +++ b/admin/tool/lp/classes/external.php @@ -205,7 +205,7 @@ class external extends external_api { self::validate_context($framework->get_context()); $output = $PAGE->get_renderer('tool_lp'); - $renderable = new output\manage_competencies_page($framework, $params['search'], $framework->get_context()); + $renderable = new output\manage_competencies_page($framework, $params['search'], $framework->get_context(), null); $data = $renderable->export_for_template($output); diff --git a/admin/tool/lp/competencies.php b/admin/tool/lp/competencies.php index 880c89a6b9c..facae66724f 100644 --- a/admin/tool/lp/competencies.php +++ b/admin/tool/lp/competencies.php @@ -25,9 +25,9 @@ require_once(__DIR__ . '/../../../config.php'); require_once($CFG->libdir.'/adminlib.php'); -$id = required_param('competencyframeworkid', PARAM_INT); $pagecontextid = required_param('pagecontextid', PARAM_INT); // Reference to the context we came from. $search = optional_param('search', '', PARAM_RAW); +$id = required_param('competencyframeworkid', PARAM_INT); require_login(); \core_competency\api::require_enabled(); diff --git a/admin/tool/lp/styles.css b/admin/tool/lp/styles.css index 77339bb6a56..61a6cb8736f 100644 --- a/admin/tool/lp/styles.css +++ b/admin/tool/lp/styles.css @@ -1,7 +1,8 @@ .path-admin-tool-lp [data-region="managecompetencies"] ul li, .path-admin-tool-lp [data-region="plans"] ul li, .path-admin-tool-lp [data-region="competencymovetree"] ul li, -.path-admin-tool-lp [data-region="competencylinktree"] ul li { +.path-admin-tool-lp [data-region="competencylinktree"] ul li, +.path-badges [data-region="competencylinktree"] ul li { list-style-type: none; } @@ -22,6 +23,7 @@ .path-admin-tool-lp [data-region="managecompetencies"] ul[data-enhance="tree"], .path-admin-tool-lp [data-region="plans"] ul[data-enhance="tree"], .path-admin-tool-lp [data-region="competencylinktree"] ul[data-enhance="linktree"], +.path-badges [data-region="competencylinktree"] ul[data-enhance="linktree"], .path-admin-tool-lp [data-region="competencymovetree"] ul[data-enhance="movetree"] { border: 1px solid #ccc; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); @@ -36,11 +38,13 @@ .path-admin-tool-lp [data-region="managecompetencies"] ul, .path-admin-tool-lp [data-region="plans"] ul, .path-admin-tool-lp [data-region="competencylinktree"] ul, +.path-badges [data-region="competencylinktree"] ul, .path-admin-tool-lp [data-region="competencymovetree"] ul { cursor: pointer; } .path-admin-tool-lp [data-region="competencylinktree"] ul li > span, +.path-badges [data-region="competencylinktree"] ul li > span, .path-admin-tool-lp [data-region="competencymovetree"] ul li > span, .path-admin-tool-lp [data-region="plans"] ul li > span, .path-admin-tool-lp [data-region="managecompetencies"] ul li > span { @@ -52,6 +56,7 @@ } .path-admin-tool-lp [data-region="competencylinktree"] ul [aria-selected="true"] > span, +.path-badges [data-region="competencylinktree"] ul [aria-selected="true"] > span, .path-admin-tool-lp [data-region="competencymovetree"] ul [aria-selected="true"] > span, .path-admin-tool-lp [data-region="plans"] ul [aria-selected="true"] > span, .path-admin-tool-lp [data-region="managecompetencies"] ul [aria-selected="true"] > span { @@ -59,6 +64,7 @@ } .path-admin-tool-lp [data-region="competencylinktree"] ul [tabindex="0"] > span, +.path-badges [data-region="competencylinktree"] ul [tabindex="0"] > span, .path-admin-tool-lp [data-region="competencymovetree"] ul [tabindex="0"] > span, .path-admin-tool-lp [data-region="plans"] ul [tabindex="0"] > span, .path-admin-tool-lp [data-region="managecompetencies"] ul [tabindex="0"] > span { @@ -74,7 +80,8 @@ text-align: center; } -.path-admin-tool-lp [data-region="competencylinktree"] > ul { +.path-admin-tool-lp [data-region="competencylinktree"] > ul, +.path-badges [data-region="competencylinktree"] > ul { overflow-y: auto; height: 400px; } @@ -112,7 +119,8 @@ .path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-outcome"] select, .path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-type"] select, -.path-admin-tool-lp [data-region="competencylinktree"] select { +.path-admin-tool-lp [data-region="competencylinktree"] select, +.path-badges [data-region="competencylinktree"] select { width: 100%; } diff --git a/badges/amd/build/competency.min.js b/badges/amd/build/competency.min.js new file mode 100644 index 00000000000..d57565f190a --- /dev/null +++ b/badges/amd/build/competency.min.js @@ -0,0 +1 @@ +define(["jquery","tool_lp/competencypicker","core/ajax","core/notification","core/templates"],function(a,b,c,d,e){var f=null,g=1,h=function(){var b=a('[data-action="competencies"]').val(),f=[],g=0;if(""!=b)for(b=b.split(","),g=0;g. + +/** + * Badge select competency actions + * + * @module core_badges/competency + * @package core + * @class competency + * @copyright 2019 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define(['jquery', 'tool_lp/competencypicker', 'core/ajax', 'core/notification', 'core/templates'], + function($, Picker, Ajax, Notification, Templates) { + + var pickerInstance = null; + + var pageContextId = 1; + + /** + * Re-render the list of selected competencies. + * + * @method renderCompetencies + */ + var renderCompetencies = function() { + var currentCompetencies = $('[data-action="competencies"]').val(); + var requests = []; + var i = 0; + + if (currentCompetencies != '') { + currentCompetencies = currentCompetencies.split(','); + for (i = 0; i < currentCompetencies.length; i++) { + requests[requests.length] = { + methodname: 'core_competency_read_competency', + args: {id: currentCompetencies[i]} + }; + } + } + + $.when.apply($, Ajax.call(requests, false)).then(function() { + var i = 0, + competencies = []; + + for (i = 0; i < arguments.length; i++) { + competencies[i] = arguments[i]; + } + var context = { + competencies: competencies + }; + + return Templates.render('core_badges/award_criteria_competencies', context); + }).then(function(html, js) { + Templates.replaceNode($('[data-region="competencies"]'), html, js); + return true; + }).fail(Notification.exception); + + return true; + }; + + /** + * Deselect a competency + * + * @method unpickCompetenciesHandler + */ + var unpickCompetenciesHandler = function(e) { + var currentCompetencies = $('[data-action="competencies"]').val().split(','), + newCompetencies = [], + i, + toRemove = $(e.currentTarget).data('id'); + + for (i = 0; i < currentCompetencies.length; i++) { + if (currentCompetencies[i] != toRemove) { + newCompetencies[newCompetencies.length] = currentCompetencies[i]; + } + } + + $('[data-action="competencies"]').val(newCompetencies.join(',')); + + return renderCompetencies(); + }; + + /** + * Open a competencies popup to relate competencies. + * + * @method pickCompetenciesHandler + */ + var pickCompetenciesHandler = function() { + var currentCompetencies = $('[data-action="competencies"]').val().split(','); + + if (!pickerInstance) { + pickerInstance = new Picker(pageContextId, false, 'parents', true); + pickerInstance.on('save', function(e, data) { + var before = $('[data-action="competencies"]').val(); + var compIds = data.competencyIds; + if (before != '') { + compIds = compIds.concat(before.split(',')); + } + var value = compIds.join(','); + + $('[data-action="competencies"]').val(value); + + return renderCompetencies(); + }); + } + + pickerInstance.setDisallowedCompetencyIDs(currentCompetencies); + pickerInstance.display(); + }; + + return /** @alias module:core_badges/competency */ { + /** + * Listen for clicks on the competency picker and push the changes to the form element. + * + * @method init + * @param {Integer} contextId + */ + init: function(contextId) { + pageContextId = contextId; + renderCompetencies(); + $('[data-action="select-competencies"]').on('click', pickCompetenciesHandler); + $('body').on('click', '[data-action="deselect-competency"]', unpickCompetenciesHandler); + } + }; +}); diff --git a/badges/classes/observer.php b/badges/classes/observer.php index 8754ab4d6e4..f33a7f01c4d 100644 --- a/badges/classes/observer.php +++ b/badges/classes/observer.php @@ -69,6 +69,48 @@ class core_badges_observer { } } + /** + * Triggered when '\core\event\competency_evidence_created' event is triggered. + * + * @param \core\event\competency_evidence_created $event + */ + public static function competency_criteria_review(\core\event\competency_evidence_created $event) { + global $DB, $CFG; + + if (!empty($CFG->enablebadges)) { + require_once($CFG->dirroot.'/lib/badgeslib.php'); + + if (!get_config('core_competency', 'enabled')) { + return; + } + + $ucid = $event->other['usercompetencyid']; + $cid = $event->other['competencyid']; + $eventdata = $event->get_record_snapshot('competency_usercomp', $ucid); + $userid = $event->relateduserid; + + if ($rs = $DB->get_records('badge_criteria_param', array('name' => 'competency_' . $cid, 'value' => $cid))) { + foreach ($rs as $r) { + $crit = $DB->get_record('badge_criteria', array('id' => $r->critid), 'badgeid, criteriatype', MUST_EXIST); + $badge = new badge($crit->badgeid); + // Only site badges are updated from site competencies. + if (!$badge->is_active() || $badge->is_issued($userid)) { + continue; + } + + if ($badge->criteria[$crit->criteriatype]->review($userid)) { + $badge->criteria[$crit->criteriatype]->mark_complete($userid); + + if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) { + $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid); + $badge->issue($userid); + } + } + } + } + } + } + /** * Triggered when 'course_completed' event is triggered. * diff --git a/badges/criteria/award_criteria.php b/badges/criteria/award_criteria.php index 0fb86e18e95..6f689d0df4b 100644 --- a/badges/criteria/award_criteria.php +++ b/badges/criteria/award_criteria.php @@ -80,20 +80,27 @@ define('BADGE_CRITERIA_TYPE_BADGE', 7); */ define('BADGE_CRITERIA_TYPE_COHORT', 8); +/* + * Competency criteria type + * Criteria type constant, primarily for storing criteria type in the database. + */ +define('BADGE_CRITERIA_TYPE_COMPETENCY', 9); + /* * Criteria type constant to class name mapping */ global $BADGE_CRITERIA_TYPES; $BADGE_CRITERIA_TYPES = array( - BADGE_CRITERIA_TYPE_OVERALL => 'overall', - BADGE_CRITERIA_TYPE_ACTIVITY => 'activity', - BADGE_CRITERIA_TYPE_MANUAL => 'manual', - BADGE_CRITERIA_TYPE_SOCIAL => 'social', - BADGE_CRITERIA_TYPE_COURSE => 'course', - BADGE_CRITERIA_TYPE_COURSESET => 'courseset', - BADGE_CRITERIA_TYPE_PROFILE => 'profile', - BADGE_CRITERIA_TYPE_BADGE => 'badge', - BADGE_CRITERIA_TYPE_COHORT => 'cohort', + BADGE_CRITERIA_TYPE_OVERALL => 'overall', + BADGE_CRITERIA_TYPE_ACTIVITY => 'activity', + BADGE_CRITERIA_TYPE_MANUAL => 'manual', + BADGE_CRITERIA_TYPE_SOCIAL => 'social', + BADGE_CRITERIA_TYPE_COURSE => 'course', + BADGE_CRITERIA_TYPE_COURSESET => 'courseset', + BADGE_CRITERIA_TYPE_PROFILE => 'profile', + BADGE_CRITERIA_TYPE_BADGE => 'badge', + BADGE_CRITERIA_TYPE_COHORT => 'cohort', + BADGE_CRITERIA_TYPE_COMPETENCY => 'competency', ); /** diff --git a/badges/criteria/award_criteria_competency.php b/badges/criteria/award_criteria_competency.php new file mode 100644 index 00000000000..9866b3b5261 --- /dev/null +++ b/badges/criteria/award_criteria_competency.php @@ -0,0 +1,237 @@ +. + +/** + * This file contains the badge earned badge award criteria type class + * + * @package core + * @subpackage badges + * @copyright 2019 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Badge award criteria -- award on competency completion + * + * @package core + * @subpackage badges + * @copyright 2019 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class award_criteria_competency extends award_criteria { + + /* @var int Criteria [BADGE_CRITERIA_TYPE_COMPETENCY] */ + public $criteriatype = BADGE_CRITERIA_TYPE_COMPETENCY; + + public $required_param = 'competency'; + public $self_validation = true; + public $optional_params = array(''); + + /** + * Get criteria details for displaying to users + * @param string $short Print short version of criteria + * @return string + */ + public function get_details($short = '') { + global $DB, $OUTPUT; + $output = array(); + + foreach ($this->params as $p) { + $competency = new \core_competency\competency($p['competency']); + if ($short) { + $competency->set('description', ''); + } + $summary = new \tool_lp\output\competency_summary($competency, $competency->get_framework(), !$short, !$short); + $str = $OUTPUT->render($summary); + $output[] = $str; + } + + return '
' . + implode('
', $output) . + '
'; + } + + /** + * Add appropriate new criteria options to the form + * @param object $mform moodle form + */ + public function get_options(&$mform) { + global $DB, $PAGE; + $none = false; + $availablebadges = null; + + $mform->addElement('header', 'first_header', $this->get_title()); + $mform->addHelpButton('first_header', 'criteria_' . $this->criteriatype, 'badges'); + + // Determine if this badge is a course badge or a site badge. + $competencies = ''; + if (count($this->params)) { + $competencies = implode(',', array_keys($this->params)); + } + $mform->addElement('static', 'competenciesdescription', '', '
'); + $mform->addElement('hidden', 'competency', $competencies, ['data-action' => 'competencies']); + + $mform->setType('competency', PARAM_RAW); + $badge = $DB->get_record('badge', array('id' => $this->badgeid)); + if ($badge->type == BADGE_TYPE_SITE) { + $context = context_system::instance(); + } else if ($badge->type == BADGE_TYPE_COURSE) { + $context = context_course::instance($badge->courseid); + } + $params = [$context->id]; + // Require some JS to select the competencies. + $PAGE->requires->js_call_amd('core_badges/competency', 'init', $params); + + $mform->addElement('button', 'select_competencies', get_string('addcompetency', 'badges'), ['data-action' => 'select-competencies']); + + // Add aggregation. + if (!$none) { + $mform->addElement('header', 'aggregation', get_string('method', 'badges')); + $agg = array(); + $agg[] =& $mform->createElement('radio', 'agg', '', get_string('allmethodcompetencies', 'badges'), 1); + $agg[] =& $mform->createElement('radio', 'agg', '', get_string('anymethodcompetencies', 'badges'), 2); + $mform->addGroup($agg, 'methodgr', '', array('
'), false); + if ($this->id !== 0) { + $mform->setDefault('agg', $this->method); + } else { + $mform->setDefault('agg', BADGE_CRITERIA_AGGREGATION_ANY); + } + } + + return array($none, get_string('noparamstoadd', 'badges')); + } + + /** + * Save criteria records + * + * @param array $params Values from the form or any other array. + */ + public function save($params = array()) { + $competencies = explode(',', $params['competency']); + unset($params['competency']); + foreach ($competencies as $competencyid) { + $params["competency_{$competencyid}"] = $competencyid; + } + parent::save($params); + } + + /** + * Review this criteria and decide if it has been completed + * + * @param int $userid User whose criteria completion needs to be reviewed. + * @param bool $filtered An additional parameter indicating that user list + * has been reduced and some expensive checks can be skipped. + * + * @return bool Whether criteria is complete. + */ + public function review($userid, $filtered = false) { + global $DB; + + $overall = false; + $competencyids = []; + + foreach ($this->params as $param) { + $competencyids[] = $param['competency']; + } + + $existing = []; + $badge = $DB->get_record('badge', array('id' => $this->badgeid)); + if ($badge->type == BADGE_TYPE_SITE) { + $existing = \core_competency\user_competency::get_multiple($userid, $competencyids); + } else if ($badge->type == BADGE_TYPE_COURSE) { + $existing = \core_competency\user_competency_course::get_multiple($userid, $badge->courseid, $competencyids); + } + + foreach ($this->params as $param) { + $found = false; + $proficiency = false; + foreach ($existing as $usercompetency) { + if ($usercompetency->get('competencyid') == $param['competency']) { + $found = true; + $proficiency = $usercompetency->get('proficiency'); + } + } + + if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) { + if (!$proficiency) { + return false; + } + } else if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) { + if ($proficiency) { + return true; + } + } + } + + return $overall; + } + + /** + * Returns array with sql code and parameters returning all ids + * of users who meet this particular criterion. + * + * @return array list($join, $where, $params) + */ + public function get_completed_criteria_sql() { + global $DB; + + $join = ''; + $where = ''; + $params = []; + $competencyids = []; + + $badge = $DB->get_record('badge', array('id' => $this->badgeid)); + + if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) { + // User has received ANY of the required competencies (we can use an in or equals list). + foreach ($this->params as $param) { + $competencyids[] = $param['competency']; + } + + $where = ' AND uc2.competencyid '; + list($sql, $params) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED, 'usercomp'); + $where .= $sql; + if ($badge->type == BADGE_TYPE_SITE) { + $join = ' JOIN {competency_usercomp} uc2 ON uc2.userid = u.id'; + } else if ($badge->type == BADGE_TYPE_COURSE) { + $join = ' JOIN {competency_usercompcourse} uc2 ON uc2.userid = u.id AND uc2.courseid = :competencycourseid '; + $params['competencycourseid'] = $badge->courseid; + } + $where .= ' AND uc2.proficiency = :isproficient '; + $params['isproficient'] = true; + return array($join, $where, $params); + } else { + + // User has received ALL of the required competencies (we have to join on each one). + $joincount = 0; + foreach ($this->params as $param) { + $joincount++; + $join .= ' JOIN {competency_usercomp} uc' . $joincount . ' ON uc' . $joincount . '.userid = u.id'; + $where .= ' AND uc' . $joincount . '.competencyid = :competencyindex' . $joincount; + $params['competencyindex' . $joincount] = $param['competency']; + + $where .= ' AND uc' . $joincount . '.userid = u.id'; + $where .= ' AND uc' . $joincount . '.proficiency = :isproficient' . $joincount; + $params['isproficient' . $joincount] = true; + } + + return array($join, $where, $params); + } + return array($join, $where, $params); + } +} diff --git a/badges/criteria_form.php b/badges/criteria_form.php index c8d18d14729..efd08ecff4f 100644 --- a/badges/criteria_form.php +++ b/badges/criteria_form.php @@ -78,7 +78,7 @@ class edit_criteria_form extends moodleform { $errors = parent::validation($data, $files); $addcourse = $this->_customdata['addcourse']; - if (!$addcourse && isset($this->_customdata['criteria']->required_param)) { + if (!$addcourse && isset($this->_customdata['criteria']->required_param) && !isset($this->_customdata['criteria']->self_validation)) { $required = $this->_customdata['criteria']->required_param; $pattern1 = '/^' . $required . '_(\d+)$/'; $pattern2 = '/^' . $required . '_(\w+)$/'; diff --git a/badges/templates/award_criteria_competencies.mustache b/badges/templates/award_criteria_competencies.mustache new file mode 100644 index 00000000000..3113597f4f1 --- /dev/null +++ b/badges/templates/award_criteria_competencies.mustache @@ -0,0 +1,70 @@ +{{! + 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 . +}} +{{! + @template core_badges/award_criteria_competencies + + List of competencies for a badge. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Context variables required for this template: + * competencies array + * id int + * shortname string + * idnumber string + + Example context (json): + { + "competencies": + [ + { + "id": 1, + "shortname": "Competency", + "idnumber": "C1" + } + ] + } + +}} +
+ +{{^competencies}} + {{#str}}nocompetencies, core_badges{{/str}} +{{/competencies}} +
+{{#js}} +require(['tool_lp/competencydialogue'], function(Compdialogue) { + Compdialogue.init(); +}); +{{/js}} diff --git a/badges/tests/behat/add_badge.feature b/badges/tests/behat/add_badge.feature index 235fdd0bfd3..c54b6936550 100644 --- a/badges/tests/behat/add_badge.feature +++ b/badges/tests/behat/add_badge.feature @@ -46,7 +46,7 @@ Feature: Add badges to the system And I should see "Test badge with 'apostrophe' and other friends (&@#)" And I should see "Endorsement" And I should see "Related badges (0)" - And I should see "External alignments (0)" + And I should see "Alignments (0)" And I should not see "Create badge" And I follow "Manage badges" And I should see "Number of badges available: 1" @@ -118,7 +118,7 @@ Feature: Add badges to the system Then I should see "Changes saved" @javascript @_file_upload - Scenario: External alignments for Badge + Scenario: Alignments for Badge Given I navigate to "Badges > Add a new badge" in site administration And I set the following fields to these values: | Name | Test Badge | @@ -133,15 +133,15 @@ Feature: Add badges to the system When I press "Create badge" Then I should see "Test Badge" And I should see "Endorsement" - And I follow "External alignments (0)" - And I should see "This badge does not have any external alignments specified." - And I press "Add external alignment" + And I follow "Alignments (0)" + And I should see "This badge does not have any external skills or standards specified." + And I press "Add external skill or standard" And I set the following fields to these values: - | External alignment name | Test Badge Alignments | + | Name | Test Badge Alignments | | URL | https://alignments.example.com | | Description | Test Badge Alignments description | When I press "Save changes" - And I should see "External alignments (1)" + And I should see "Alignments (1)" @javascript @_file_upload Scenario: Add a badge from Site badges section @@ -169,7 +169,7 @@ Feature: Add badges to the system And I should see "Test badge with 'apostrophe' and other friends (&@#) 2" And I should see "Endorsement" And I should see "Related badges (0)" - And I should see "Competencies (0)" + And I should see "Alignments (0)" And I should not see "Create badge" And I follow "Manage badges" And I should see "Number of badges available: 1" diff --git a/badges/tests/behat/criteria_competency.feature b/badges/tests/behat/criteria_competency.feature new file mode 100644 index 00000000000..17b4eb230ec --- /dev/null +++ b/badges/tests/behat/criteria_competency.feature @@ -0,0 +1,119 @@ +@core @core_badges @_file_upload +Feature: Award badges based on competency completion + In order to award badges to users based on competency completion + As an admin + I need to add competency completion criteria to badges in the system + + Background: Setup the competency framework and the course + Given the following "users" exist: + | username | firstname | lastname | email | + | user1 | First | User | first@example.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | user1 | C1 | student | + And the following lp "frameworks" exist: + | shortname | idnumber | + | Framework 1 | sc-y-2 | + And the following lp "competencies" exist: + | shortname | framework | + | comp1 | sc-y-2 | + And I log in as "admin" + + @javascript + Scenario: Award badge for completing a competency in a course + # Add a competency to the course + When I am on "Course 1" course homepage + And I follow "Competencies" + And I press "Add competencies to course" + And "Competency picker" "dialogue" should be visible + And I select "comp1" of the competency tree + And I click on "Add" "button" in the "Competency picker" "dialogue" + And I click on "Edit" "link" in the "[data-region='configurecoursecompetencies']" "css_element" + And I click on "Rating a competency only updates the competency in this course" "text" + And I click on "Save changes" "button" in the "Configure course competencies" "dialogue" + # Add a badge to the course + And I am on "Course 1" course homepage + And I navigate to "Badges > Add a new badge" in current page administration + And I follow "Add a new badge" + And I set the following fields to these values: + | Name | Course Badge | + | Description | Course badge description | + | issuername | Tester of course badge | + And I upload "badges/tests/behat/badge.png" file to "Image" filemanager + And I press "Create badge" + # Set the competency as a criteria for the badge + And I set the field "type" to "Competencies" + And I press "Add competency" + And "Competency picker" "dialogue" should be visible + And I select "comp1" of the competency tree + And I click on "Add" "button" in the "Competency picker" "dialogue" + # And I wait "1" seconds + And I wait until the page is ready + And I press "Save" + # And I wait "1" seconds + And I wait until the page is ready + # Enable the badge + And I press "Enable access" + And I press "Continue" + # Rate the competency in the course + And I am on "Course 1" course homepage + And I follow "Competencies" + And I click on "comp1" "link" in the "[data-region='coursecompetencies']" "css_element" + And I press "Rate" + And I set the following fields to these values: + | Rating | C | + And I click on "Rate" "button" in the "Rate" "dialogue" + And I log out + # See if we got the badge + Then I log in as "user1" + And I follow "Profile" in the user menu + And I should see "Course Badge" + + @javascript + Scenario: Award badge for completing a competency in the site + # Add a competency to the course + When I am on "Course 1" course homepage + And I follow "Competencies" + And I press "Add competencies to course" + And "Competency picker" "dialogue" should be visible + And I select "comp1" of the competency tree + And I click on "Add" "button" in the "Competency picker" "dialogue" + # Add a badge to the site + And I navigate to "Badges > Add a new badge" in site administration + And I set the following fields to these values: + | Name | Site Badge | + | Description | Site badge description | + | issuername | Tester of site badge | + And I upload "badges/tests/behat/badge.png" file to "Image" filemanager + And I press "Create badge" + # Set the competency as a criteria for the badge + And I set the field "type" to "Competencies" + And I press "Add competency" + And "Competency picker" "dialogue" should be visible + And I select "comp1" of the competency tree + And I click on "Add" "button" in the "Competency picker" "dialogue" + # And I wait "1" seconds + And I wait until the page is ready + And I press "Save" + # Enable the badge + # And I wait "1" seconds + And I wait until the page is ready + And I press "Enable access" + And I press "Continue" + # Rate the competency in the course + And I am on "Course 1" course homepage + And I follow "Competencies" + And I click on "comp1" "link" in the "[data-region='coursecompetencies']" "css_element" + And I press "Rate" + And I set the following fields to these values: + | Rating | C | + And I wait until the page is ready + And I click on "Rate" "button" in the "Rate" "dialogue" + And I log out + # See if we got the badge + Then I log in as "user1" + And I follow "Profile" in the user menu + And I should see "Site Badge" diff --git a/lang/en/badges.php b/lang/en/badges.php index 435c7c8190d..a959a438f4e 100644 --- a/lang/en/badges.php +++ b/lang/en/badges.php @@ -27,9 +27,11 @@ $string['actions'] = 'Actions'; $string['activate'] = 'Enable access'; $string['activatesuccess'] = 'Access to the badges was successfully enabled.'; -$string['addalignment'] = 'Add competency'; +$string['addalignment'] = 'Add external skill or standard'; $string['addbadge'] = 'Add badges'; $string['addbadge_help'] = 'Select all badges that should be added to this badge requirement. Hold CTRL key to select multiple items.'; +$string['addcompetency'] = 'Add competency'; +$string['addcompetency_help'] = 'Select all competencies that should be added to this badge requirement. Hold CTRL key to select multiple items.'; $string['addbadgecriteria'] = 'Add badge criteria'; $string['addcriteria'] = 'Add criteria'; $string['addcriteriatext'] = 'To start adding criteria, please select one of the options from the drop-down menu.'; @@ -42,12 +44,13 @@ $string['addtobackpack'] = 'Add to backpack'; $string['adminonly'] = 'This page is restricted to site administrators only.'; $string['after'] = 'after the date of issue.'; $string['aggregationmethod'] = 'Aggregation method'; -$string['alignment'] = 'Competency'; +$string['alignment'] = 'Alignment'; $string['all'] = 'All'; $string['allmethod'] = 'All of the selected conditions are met'; $string['allmethodactivity'] = 'All of the selected activities are complete'; $string['allmethodbadges'] = 'All of the selected badges have been earned'; $string['allmethodcohort'] = 'Membership in all the selected cohorts'; +$string['allmethodcompetencies'] = 'All of the selected competencies have been completed'; $string['allmethodcourseset'] = 'All of the selected courses are complete'; $string['allmethodmanual'] = 'All of the selected roles award the badge'; $string['allmethodprofile'] = 'All of the selected profile fields have been completed'; @@ -62,6 +65,7 @@ $string['anymethod'] = 'Any of the selected conditions is met'; $string['anymethodactivity'] = 'Any of the selected activities is complete'; $string['anymethodbadges'] = 'Any of the selected badges have been earned'; $string['anymethodcohort'] = 'Membership in any of the selected cohorts'; +$string['anymethodcompetencies'] = 'Any of the selected competencies have been completed'; $string['anymethodcourseset'] = 'Any of the selected courses is complete'; $string['anymethodmanual'] = 'Any of the selected roles awards the badge'; $string['anymethodprofile'] = 'Any of the selected profile fields has been completed'; @@ -157,7 +161,7 @@ $string['bendorsement'] = 'Endorsement'; $string['bmessage'] = 'Message'; $string['boverview'] = 'Overview'; $string['brelated'] = 'Related badges ({$a})'; -$string['balignment'] = 'Competencies ({$a})'; +$string['balignment'] = 'Alignments ({$a})'; $string['bydate'] = ' complete by'; $string['imagecaption'] = 'Image caption'; $string['imagecaption_help'] = 'If specified, an image caption is displayed on the badge page.'; @@ -197,6 +201,7 @@ $string['criteria_descr_short5'] = 'Complete {$a} of: '; $string['criteria_descr_short6'] = 'Complete {$a} of: '; $string['criteria_descr_short7'] = 'Complete {$a} of: '; $string['criteria_descr_short8'] = 'Cohort membership in {$a} of: '; +$string['criteria_descr_short9'] = 'Complete {$a} of: '; $string['criteria_descr_single_short1'] = 'Complete: '; $string['criteria_descr_single_short2'] = 'Awarded by: '; $string['criteria_descr_single_short4'] = 'Complete the course '; @@ -204,6 +209,7 @@ $string['criteria_descr_single_short5'] = 'Complete: '; $string['criteria_descr_single_short6'] = 'Complete: '; $string['criteria_descr_single_short7'] = 'Complete: '; $string['criteria_descr_single_short8'] = 'Membership in: '; +$string['criteria_descr_single_short9'] = 'Complete: '; $string['criteria_descr_single_1'] = 'The following activity has to be completed:'; $string['criteria_descr_single_2'] = 'This badge has to be awarded by a user with the following role:'; $string['criteria_descr_single_4'] = 'Users must complete the course'; @@ -211,6 +217,7 @@ $string['criteria_descr_single_5'] = 'The following course has to be completed:' $string['criteria_descr_single_6'] = 'The following user profile field has to be completed:'; $string['criteria_descr_single_7'] = 'The following badge has to be earned:'; $string['criteria_descr_single_8'] = 'Membership in the following cohort is required:'; +$string['criteria_descr_single_9'] = 'The following competencies have to be completed:'; $string['criteria_descr_0'] = 'Users are awarded this badge when they complete {$a} of the listed requirements.'; $string['criteria_descr_1'] = '{$a} of the following activities are completed:'; $string['criteria_descr_2'] = 'This badge has to be awarded by the users with {$a} of the following roles:'; @@ -219,6 +226,7 @@ $string['criteria_descr_5'] = '{$a} of the following courses ha $string['criteria_descr_6'] = '{$a} of the following user profile fields have to be completed:'; $string['criteria_descr_7'] = '{$a} of the following badges have to be earned:'; $string['criteria_descr_8'] = 'Membership in {$a} of the following cohorts is required:'; +$string['criteria_descr_9'] = '{$a} of the following competencies have to be completed:'; $string['criteria_0'] = 'This badge is awarded when...'; $string['criteria_1'] = 'Activity completion'; $string['criteria_1_help'] = 'Allows a badge to be awarded to users based on the completion of a set of activities within a course.'; @@ -236,6 +244,8 @@ $string['criteria_7'] = 'Awarded badges'; $string['criteria_7_help'] = 'Allows a badge to be awarded to users based on the other badges thay have earned.'; $string['criteria_8'] = 'Cohort membership'; $string['criteria_8_help'] = 'Allows a badge to be awarded to users based on cohort membership.'; +$string['criteria_9'] = 'Competencies'; +$string['criteria_9_help'] = 'Allows a badge to be awarded to users based on the competencies thay have completed.'; $string['criterror'] = 'Current parameters issues'; $string['criterror_help'] = 'This fieldset shows all parameters that were initially added to this badge requirement but are no longer available. It is recommended that you un-check such parameters to make sure that users can earn this badge in the future.'; $string['currentimage'] = 'Current image'; @@ -379,12 +389,13 @@ $string['mybackpack'] = 'My backpack settings'; $string['never'] = 'Never'; $string['newbadge'] = 'Add a new badge'; $string['newimage'] = 'New image'; -$string['noalignment'] = 'This badge does not have any competencies specified.'; +$string['noalignment'] = 'This badge does not have any external skills or standards specified.'; $string['noawards'] = 'This badge has not been earned yet.'; $string['nobackpack'] = 'There is no backpack service connected to this account.
'; $string['nobackpackbadges'] = 'There are no badges in the collections you have selected. Add more collections.'; $string['nobackpackcollections'] = 'No badge collections have been selected. Add collections.'; $string['nobadges'] = 'There are no badges available.'; +$string['nocompetencies'] = 'No competencies selected.'; $string['nocriteria'] = 'Criteria for this badge have not been set up yet.'; $string['noendorsement'] = 'This badge does not have an endorsement.'; $string['noexpiry'] = 'This badge does not have an expiry date.'; @@ -393,7 +404,7 @@ $string['norelated'] = 'This badge does not have any related badges.'; $string['notacceptedrole'] = 'Your current role assignment is not among the roles that can manually issue this badge.
If you would like to see users who have already earned this badge, you can visit {$a} page. '; $string['notconnected'] = 'Not connected'; -$string['notealignment'] = 'External competencies, skills or standards which the badge covers may be specified. Any competencies are displayed on the badge page.'; +$string['notealignment'] = 'External skills or standards, which the badge is aligned with, may be specified. Any external skills or standards are displayed on the badge page.'; $string['noteendorsement'] = 'An endorsement from a third party may be used to add value to the badge. For example, a badge issued by a teacher may be endorsed by the school, or a badge issued by a local awarding body may be endorsed by the national awarding body.'; $string['noterelated'] = 'Badges with a connection may be marked as related. For example, badges with the same criteria which are displayed in different languages may be marked as related. Any related badges are displayed on the badge page.'; $string['nothingtoadd'] = 'There are no available criteria to add.'; @@ -495,16 +506,16 @@ Once a badge has been issued to at least one user, it automatically becomes **LO We want to make sure that all users complete the same requirements to earn a badge. Currently, it is not possible to revoke badges. If we allowed badges requirements to be modified all the time, we would most likely end up with users having the same badge for meeting completely different requirements.'; $string['subject'] = 'Message subject'; -$string['targetname'] = 'Competency name'; -$string['targetname_help'] = 'The competency, skill or standard which the badge covers.'; +$string['targetname'] = 'Name'; +$string['targetname_help'] = 'The external skill or standard which the badge is aligned with.'; $string['targeturl'] = 'URL'; -$string['targeturl_help'] = 'A link to a page describing the competency, skill or standard. The URL should have a prefix http:// or https://.'; +$string['targeturl_help'] = 'A link to a page describing the external skill or standard. The URL should have a prefix http:// or https://.'; $string['targetdescription'] = 'Description'; -$string['targetdescription_help'] = 'Short description of the alignment target.'; +$string['targetdescription_help'] = 'Short description of the external skill or standard.'; $string['targetframework'] = 'Framework'; -$string['targetframework_help'] = 'The name of the competency framework.'; +$string['targetframework_help'] = 'The name of the external skill or standard framework.'; $string['targetcode'] = 'Code'; -$string['targetcode_help'] = 'A unique string identifier for referencing the competency within its framework.'; +$string['targetcode_help'] = 'A unique string identifier for referencing the external skill or standard within its framework.'; $string['type'] = 'Type'; $string['variablesubstitution'] = 'Variable substitution in messages.'; $string['variablesubstitution_help'] = 'In a badge message, certain variables can be inserted into the subject and/or body of a message so that they will be replaced with real values when the message is sent. The variables should be inserted into the text exactly as they are shown below. The following variables can be used: diff --git a/lib/badgeslib.php b/lib/badgeslib.php index 98c1b9e3746..a95d2371b39 100644 --- a/lib/badgeslib.php +++ b/lib/badgeslib.php @@ -226,7 +226,8 @@ class badge { BADGE_CRITERIA_TYPE_MANUAL, BADGE_CRITERIA_TYPE_COURSE, BADGE_CRITERIA_TYPE_BADGE, - BADGE_CRITERIA_TYPE_ACTIVITY + BADGE_CRITERIA_TYPE_ACTIVITY, + BADGE_CRITERIA_TYPE_COMPETENCY ); } else if ($this->type == BADGE_TYPE_SITE) { $criteriatypes = array( @@ -236,6 +237,7 @@ class badge { BADGE_CRITERIA_TYPE_BADGE, BADGE_CRITERIA_TYPE_PROFILE, BADGE_CRITERIA_TYPE_COHORT, + BADGE_CRITERIA_TYPE_COMPETENCY ); } @@ -508,6 +510,7 @@ class badge { } list($extrajoin, $extrawhere, $extraparams) = $crit->get_completed_criteria_sql(); + // For site level badges, get all active site users who can earn this badge and haven't got it yet. if ($this->type == BADGE_TYPE_SITE) { $sql = "SELECT DISTINCT u.id, bi.badgeid @@ -838,7 +841,7 @@ class badge { * * @return array List content alignments. */ - public function get_alignment() { + public function get_alignments() { global $DB; return $DB->get_records('badge_alignment', array('badgeid' => $this->id)); } diff --git a/lib/db/events.php b/lib/db/events.php index 0e1e3e17b1c..2be83ee60c6 100644 --- a/lib/db/events.php +++ b/lib/db/events.php @@ -62,7 +62,10 @@ $observers = array( 'eventname' => '\core\event\cohort_member_added', 'callback' => 'core_badges_observer::cohort_criteria_review', ), - + array( + 'eventname' => '\core\event\competency_evidence_created', + 'callback' => 'core_badges_observer::competency_criteria_review', + ), // Competencies. array( 'eventname' => '\core\event\course_completed', From da5e19a12816474ea054e4c68b206908d5b8791b Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Wed, 23 Jan 2019 16:29:52 +0800 Subject: [PATCH 3/8] MDL-63876 badges: Do not call tool_lp directly --- .../amd/build/form_competency_element.min.js | 1 + .../lp/amd/src/form_competency_element.js | 9 +- .../site_competencies_form_element.php | 99 +++++++++++++++++++ admin/tool/lp/lang/en/tool_lp.php | 1 + admin/tool/lp/lib.php | 60 +++++++++++ .../templates/form_competency_list.mustache | 6 +- badges/amd/build/competency.min.js | 1 - badges/criteria/award_criteria_competency.php | 37 ++++--- .../tests/behat/criteria_competency.feature | 12 +-- 9 files changed, 196 insertions(+), 30 deletions(-) create mode 100644 admin/tool/lp/amd/build/form_competency_element.min.js rename badges/amd/src/competency.js => admin/tool/lp/amd/src/form_competency_element.js (95%) create mode 100644 admin/tool/lp/classes/site_competencies_form_element.php rename badges/templates/award_criteria_competencies.mustache => admin/tool/lp/templates/form_competency_list.mustache (92%) delete mode 100644 badges/amd/build/competency.min.js diff --git a/admin/tool/lp/amd/build/form_competency_element.min.js b/admin/tool/lp/amd/build/form_competency_element.min.js new file mode 100644 index 00000000000..20679d9384e --- /dev/null +++ b/admin/tool/lp/amd/build/form_competency_element.min.js @@ -0,0 +1 @@ +define(["jquery","tool_lp/competencypicker","core/ajax","core/notification","core/templates"],function(a,b,c,d,e){var f=null,g=1,h=function(){var b=a('[data-action="competencies"]').val(),f=[],g=0;if(""!=b)for(b=b.split(","),g=0;g * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -60,7 +59,7 @@ define(['jquery', 'tool_lp/competencypicker', 'core/ajax', 'core/notification', competencies: competencies }; - return Templates.render('core_badges/award_criteria_competencies', context); + return Templates.render('tool_lp/form_competency_list', context); }).then(function(html, js) { Templates.replaceNode($('[data-region="competencies"]'), html, js); return true; @@ -119,7 +118,7 @@ define(['jquery', 'tool_lp/competencypicker', 'core/ajax', 'core/notification', pickerInstance.display(); }; - return /** @alias module:core_badges/competency */ { + return /** @alias module:tool_lp/form_competency_element */ { /** * Listen for clicks on the competency picker and push the changes to the form element. * diff --git a/admin/tool/lp/classes/site_competencies_form_element.php b/admin/tool/lp/classes/site_competencies_form_element.php new file mode 100644 index 00000000000..270d8a221b4 --- /dev/null +++ b/admin/tool/lp/classes/site_competencies_form_element.php @@ -0,0 +1,99 @@ +. + + +/** + * Course competencies element. + * + * @package tool_lp + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; + +require_once($CFG->libdir . '/form/hidden.php'); + +/** + * Course competencies element. + * + * @package tool_lp + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tool_lp_site_competencies_form_element extends MoodleQuickForm_hidden { + + /** + * Constructor + * + * @param string $elementName Element name + * @param mixed $elementLabel Label(s) for an element + * @param array $options Options to control the element's display + * @param mixed $attributes Either a typical HTML attribute string or an associative array. + */ + public function __construct($elementName=null, $value='', $attributes=null) { + global $OUTPUT; + + if ($elementName == null) { + // This is broken quickforms messing with the constructors. + return; + } + $attributes = array_merge(['data-action' => 'competencies'], $attributes?$attributes:[]); + + parent::__construct($elementName, $value, $attributes); + $this->setType('hidden'); + } + + /** + * Generate the hidden field and the controls to show and pick the competencies. + */ + function toHtml(){ + global $PAGE; + + $html = parent::toHTML(); + + if (!$this->isFrozen()) { + $context = context_system::instance(); + $params = [$context->id]; + // Require some JS to select the competencies. + $PAGE->requires->js_call_amd('tool_lp/form_competency_element', 'init', $params); + $html .= '
'; + $html .= '
'; + $html .= '
'; + $html .= '
'; + $html .= ''; + $html .= '
'; + $html .= '
'; + } + return $html; + } + + /** + * Accepts a renderer + * + * @param object An HTML_QuickForm_Renderer object + * @access public + * @return void + */ + function accept(&$renderer, $required=false, $error=null) + { + $renderer->renderElement($this, false, ''); + } +} diff --git a/admin/tool/lp/lang/en/tool_lp.php b/admin/tool/lp/lang/en/tool_lp.php index 8de80cdcee8..156299cd954 100644 --- a/admin/tool/lp/lang/en/tool_lp.php +++ b/admin/tool/lp/lang/en/tool_lp.php @@ -148,6 +148,7 @@ $string['nocompetenciesincourse'] = 'No competencies have been linked to this co $string['nocompetenciesinevidence'] = 'No competencies have been linked to this evidence.'; $string['nocompetenciesinlearningplan'] = 'No competencies have been linked to this learning plan.'; $string['nocompetenciesintemplate'] = 'No competencies have been linked to this learning plan template.'; +$string['nocompetenciesinlist'] = 'No competencies have been selected.'; $string['nocompetencyframeworks'] = 'No competency frameworks have been created yet.'; $string['nocompetencyselected'] = 'No competency selected'; $string['nocrossreferencedcompetencies'] = 'No other competencies have been cross-referenced to this competency.'; diff --git a/admin/tool/lp/lib.php b/admin/tool/lp/lib.php index c68729b6c95..55130165e1d 100644 --- a/admin/tool/lp/lib.php +++ b/admin/tool/lp/lib.php @@ -217,3 +217,63 @@ function tool_lp_get_fontawesome_icon_map() { 'tool_lp:url' => 'fa-external-link' ]; } + +/** + * Render a short bit of information about a competency. + */ +function tool_lp_render_competency_summary(\core_competency\competency $competency, + \core_competency\competency_framework $framework, + $includerelated, + $includecourses) { + global $PAGE; + + if (!get_config('core_competency', 'enabled')) { + return; + } + + $summary = new \tool_lp\output\competency_summary($competency, $framework, $includerelated, $includecourses); + + $output = $PAGE->get_renderer('tool_lp'); + + return $output->render($summary); +} + +/** + * Inject a course competency picker into the form. + * + * @param MoodleQuickForm $mform The actual form object (required to modify the form). + * @param integer $courseid - SITEID or a course id + * @param context $context - The page context + * @param string $elementname - The name of the form element to create + */ +function tool_lp_competency_picker($mform, $courseid, $context, $elementname) { + global $CFG, $COURSE; + + if (!get_config('core_competency', 'enabled')) { + return; + } + + if ($courseid == SITEID) { + if (!has_capability('moodle/competency:competencymanage', $context)) { + return; + } + + MoodleQuickForm::registerElementType('site_competencies', + "$CFG->dirroot/$CFG->admin/tool/lp/classes/site_competencies_form_element.php", + 'tool_lp_site_competencies_form_element'); + $mform->addElement('site_competencies', $elementname); + } else { + if (!has_capability('moodle/competency:coursecompetencymanage', $context)) { + return; + } + + MoodleQuickForm::registerElementType('course_competencies', + "$CFG->dirroot/$CFG->admin/tool/lp/classes/course_competencies_form_element.php", + 'tool_lp_course_competencies_form_element'); + $options = array( + 'courseid' => $COURSE->id + ); + $mform->addElement('course_competencies', $elementname, get_string('modcompetencies', 'tool_lp'), $options); + } + $mform->setType($elementname, PARAM_SEQUENCE); +} diff --git a/badges/templates/award_criteria_competencies.mustache b/admin/tool/lp/templates/form_competency_list.mustache similarity index 92% rename from badges/templates/award_criteria_competencies.mustache rename to admin/tool/lp/templates/form_competency_list.mustache index 3113597f4f1..b33d886afad 100644 --- a/badges/templates/award_criteria_competencies.mustache +++ b/admin/tool/lp/templates/form_competency_list.mustache @@ -15,9 +15,9 @@ along with Moodle. If not, see . }} {{! - @template core_badges/award_criteria_competencies + @template tool_lp/form_competency_list - List of competencies for a badge. + List of competencies for a form element. Classes required for JS: * none @@ -60,7 +60,7 @@ {{/competencies}} {{^competencies}} - {{#str}}nocompetencies, core_badges{{/str}} + {{#str}}nocompetenciesinlist, tool_lp{{/str}} {{/competencies}} {{#js}} diff --git a/badges/amd/build/competency.min.js b/badges/amd/build/competency.min.js deleted file mode 100644 index d57565f190a..00000000000 --- a/badges/amd/build/competency.min.js +++ /dev/null @@ -1 +0,0 @@ -define(["jquery","tool_lp/competencypicker","core/ajax","core/notification","core/templates"],function(a,b,c,d,e){var f=null,g=1,h=function(){var b=a('[data-action="competencies"]').val(),f=[],g=0;if(""!=b)for(b=b.split(","),g=0;gset('description', ''); } - $summary = new \tool_lp\output\competency_summary($competency, $competency->get_framework(), !$short, !$short); - $str = $OUTPUT->render($summary); - $output[] = $str; + if ($pluginsfunction = get_plugins_with_function('render_competency_summary')) { + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $output[] = $pluginfunction($competency, $competency->get_framework(), !$short, !$short); + } + } + } } return '
' . @@ -83,21 +87,25 @@ class award_criteria_competency extends award_criteria { if (count($this->params)) { $competencies = implode(',', array_keys($this->params)); } - $mform->addElement('static', 'competenciesdescription', '', '
'); - $mform->addElement('hidden', 'competency', $competencies, ['data-action' => 'competencies']); - - $mform->setType('competency', PARAM_RAW); $badge = $DB->get_record('badge', array('id' => $this->badgeid)); + $context = null; + $courseid = 0; + if ($badge->type == BADGE_TYPE_SITE) { $context = context_system::instance(); + $courseid = SITEID; } else if ($badge->type == BADGE_TYPE_COURSE) { $context = context_course::instance($badge->courseid); + $courseid = $badge->courseid; } - $params = [$context->id]; - // Require some JS to select the competencies. - $PAGE->requires->js_call_amd('core_badges/competency', 'init', $params); - - $mform->addElement('button', 'select_competencies', get_string('addcompetency', 'badges'), ['data-action' => 'select-competencies']); + if ($pluginsfunction = get_plugins_with_function('competency_picker')) { + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $output[] = $pluginfunction($mform, $courseid, $context, 'competency'); + } + } + } + $mform->getElement('competency')->setValue($competencies); // Add aggregation. if (!$none) { @@ -122,8 +130,11 @@ class award_criteria_competency extends award_criteria { * @param array $params Values from the form or any other array. */ public function save($params = array()) { - $competencies = explode(',', $params['competency']); + $competencies = $params['competency']; unset($params['competency']); + if (is_string($competencies)) { + $competencies = explode(',', $competencies); + } foreach ($competencies as $competencyid) { $params["competency_{$competencyid}"] = $competencyid; } diff --git a/badges/tests/behat/criteria_competency.feature b/badges/tests/behat/criteria_competency.feature index 17b4eb230ec..3a8874f3d51 100644 --- a/badges/tests/behat/criteria_competency.feature +++ b/badges/tests/behat/criteria_competency.feature @@ -31,7 +31,9 @@ Feature: Award badges based on competency completion And "Competency picker" "dialogue" should be visible And I select "comp1" of the competency tree And I click on "Add" "button" in the "Competency picker" "dialogue" + And I wait until the page is ready And I click on "Edit" "link" in the "[data-region='configurecoursecompetencies']" "css_element" + And I wait until the page is ready And I click on "Rating a competency only updates the competency in this course" "text" And I click on "Save changes" "button" in the "Configure course competencies" "dialogue" # Add a badge to the course @@ -46,14 +48,10 @@ Feature: Award badges based on competency completion And I press "Create badge" # Set the competency as a criteria for the badge And I set the field "type" to "Competencies" - And I press "Add competency" - And "Competency picker" "dialogue" should be visible - And I select "comp1" of the competency tree - And I click on "Add" "button" in the "Competency picker" "dialogue" - # And I wait "1" seconds + When I open the autocomplete suggestions list + And I click on "ul[class='form-autocomplete-suggestions'] li" "css_element" And I wait until the page is ready And I press "Save" - # And I wait "1" seconds And I wait until the page is ready # Enable the badge And I press "Enable access" @@ -95,11 +93,9 @@ Feature: Award badges based on competency completion And "Competency picker" "dialogue" should be visible And I select "comp1" of the competency tree And I click on "Add" "button" in the "Competency picker" "dialogue" - # And I wait "1" seconds And I wait until the page is ready And I press "Save" # Enable the badge - # And I wait "1" seconds And I wait until the page is ready And I press "Enable access" And I press "Continue" From 15e6a76e6f930121de1be188d919e7a4fe5cd9ce Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Fri, 25 Jan 2019 11:04:59 +0800 Subject: [PATCH 4/8] MDL-63876 competencies: Use direct URL Instead of showing competencies in a dialogue, give them their own full url to display the competency in the framework it belongs to. This is useful for exported data that won't have access to javascript. --- .../tool/lp/amd/build/competencyactions.min.js | 2 +- admin/tool/lp/amd/build/competencytree.min.js | 2 +- admin/tool/lp/amd/src/competencyactions.js | 1 + admin/tool/lp/amd/src/competencytree.js | 8 ++++++-- .../external/competency_summary_exporter.php | 7 ++++++- .../classes/output/manage_competencies_page.php | 8 +++++++- admin/tool/lp/competencies.php | 17 +++++++++++++---- .../tool/lp/templates/competency_path.mustache | 2 +- .../lp/templates/competency_summary.mustache | 2 +- .../templates/manage_competencies_page.mustache | 3 ++- 10 files changed, 39 insertions(+), 13 deletions(-) diff --git a/admin/tool/lp/amd/build/competencyactions.min.js b/admin/tool/lp/amd/build/competencyactions.min.js index 143c935e794..993a3ef943b 100644 --- a/admin/tool/lp/amd/build/competencyactions.min.js +++ b/admin/tool/lp/amd/build/competencyactions.min.js @@ -1 +1 @@ -define(["jquery","core/url","core/templates","core/notification","core/str","core/ajax","tool_lp/dragdrop-reorder","tool_lp/tree","tool_lp/dialogue","tool_lp/menubar","tool_lp/competencypicker","tool_lp/competency_outcomes","tool_lp/competencyruleconfig"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){var n,o,p,q,r,s,t=null,u=null,v=null,w=null,x=function(){var c=a('[data-region="competencyactions"]').data("competency"),f={competencyframeworkid:t.getCompetencyFrameworkId(),pagecontextid:n};null!==c&&(f.parentid=c.id);var g=function(){var c=a.param(f);window.location=b.relativeUrl("/admin/tool/lp/editcompetency.php?"+c)};null!==c&&t.hasRule(c.id)?e.get_strings([{key:"confirm",component:"moodle"},{key:"addingcompetencywillresetparentrule",component:"tool_lp",param:c.shortname},{key:"yes",component:"core"},{key:"no",component:"core"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],g)}).fail(d.exception):g()},y=function(){var b=a('[data-region="filtercompetencies"]').data("frameworkid"),c=f.call([{methodname:"core_competency_set_parent_competency",args:{competencyid:u,parentid:v}},{methodname:"tool_lp_data_for_competencies_manage_page",args:{competencyframeworkid:b,search:a('[data-region="filtercompetencies"] input').val()}}]);c[1].done(E).fail(d.exception)},z=function(){if(v="undefined"==typeof v?0:v,v!=u){var a=t.getCompetency(v)||{},b=t.getCompetency(u)||{},c="movecompetencywillresetrules",f=!1;b.parentid!=v&&(a.path&&a.path.indexOf("/"+b.id+"/")>=0&&(c="movecompetencytochildofselfwillresetrules",f=f||t.hasRule(b.id)),f=f||t.hasRule(a.id)||t.hasRule(b.parentid),f?e.get_strings([{key:"confirm",component:"moodle"},{key:c,component:"tool_lp"},{key:"yes",component:"moodle"},{key:"no",component:"moodle"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],y)}).fail(d.exception):y())}},A=function(b){var c=a(b.getContent()),d=c.find("[data-enhance=movetree]"),e=new h(d,(!1));e.on("selectionchanged",function(b,c){var d=c.selected;v=a(d).data("id")}),d.show(),c.on("click",'[data-action="move"]',function(){b.close(),z()}),c.on("click",'[data-action="cancel"]',function(){b.close()})},B=function(a,b){var c;for(c=0;cspan",O).on("dragover","li>span",P).on("dragenter","li>span",Q).on("dragleave","li>span",R).on("drop","li>span",S),b.on("selectionchanged",$),p=new m(t,s),p.on("save",L.bind(this))}}}); \ No newline at end of file +define(["jquery","core/url","core/templates","core/notification","core/str","core/ajax","tool_lp/dragdrop-reorder","tool_lp/tree","tool_lp/dialogue","tool_lp/menubar","tool_lp/competencypicker","tool_lp/competency_outcomes","tool_lp/competencyruleconfig"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){var n,o,p,q,r,s,t=null,u=null,v=null,w=null,x=function(){var c=a('[data-region="competencyactions"]').data("competency"),f={competencyframeworkid:t.getCompetencyFrameworkId(),pagecontextid:n};null!==c&&(f.parentid=c.id);var g=function(){var c=a.param(f);window.location=b.relativeUrl("/admin/tool/lp/editcompetency.php?"+c)};null!==c&&t.hasRule(c.id)?e.get_strings([{key:"confirm",component:"moodle"},{key:"addingcompetencywillresetparentrule",component:"tool_lp",param:c.shortname},{key:"yes",component:"core"},{key:"no",component:"core"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],g)}).fail(d.exception):g()},y=function(){var b=a('[data-region="filtercompetencies"]').data("frameworkid"),c=f.call([{methodname:"core_competency_set_parent_competency",args:{competencyid:u,parentid:v}},{methodname:"tool_lp_data_for_competencies_manage_page",args:{competencyframeworkid:b,search:a('[data-region="filtercompetencies"] input').val()}}]);c[1].done(E).fail(d.exception)},z=function(){if(v="undefined"==typeof v?0:v,v!=u){var a=t.getCompetency(v)||{},b=t.getCompetency(u)||{},c="movecompetencywillresetrules",f=!1;b.parentid!=v&&(a.path&&a.path.indexOf("/"+b.id+"/")>=0&&(c="movecompetencytochildofselfwillresetrules",f=f||t.hasRule(b.id)),f=f||t.hasRule(a.id)||t.hasRule(b.parentid),f?e.get_strings([{key:"confirm",component:"moodle"},{key:c,component:"tool_lp"},{key:"yes",component:"moodle"},{key:"no",component:"moodle"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],y)}).fail(d.exception):y())}},A=function(b){var c=a(b.getContent()),d=c.find("[data-enhance=movetree]"),e=new h(d,(!1));e.on("selectionchanged",function(b,c){var d=c.selected;v=a(d).data("id")}),d.show(),c.on("click",'[data-action="move"]',function(){b.close(),z()}),c.on("click",'[data-action="cancel"]',function(){b.close()})},B=function(a,b){var c;for(c=0;cspan",O).on("dragover","li>span",P).on("dragenter","li>span",Q).on("dragleave","li>span",R).on("drop","li>span",S),b.on("selectionchanged",$),p=new m(t,s),p.on("save",L.bind(this))}}}); \ No newline at end of file diff --git a/admin/tool/lp/amd/build/competencytree.min.js b/admin/tool/lp/amd/build/competencytree.min.js index 4de971d440e..3c19f042bb1 100644 --- a/admin/tool/lp/amd/build/competencytree.min.js +++ b/admin/tool/lp/amd/build/competencytree.min.js @@ -1 +1 @@ -define(["core/ajax","core/notification","core/templates","tool_lp/tree","tool_lp/competency_outcomes","jquery"],function(a,b,c,d,e,f){var g={},h=0,i="",j="",k="",l=!1,m=function(a,b){var c=0,d=!1;for(a.haschildren=!1,a.children=[],c=0;c0},hasRule:function(a){var b=this.getCompetency(a);return!!b&&(b.ruleoutcome!=e.OUTCOME_NONE&&b.ruletype)},reloadCompetencies:function(){return n("").fail(b.exception)},listCompetencies:function(){return g}}}); \ No newline at end of file +define(["core/ajax","core/notification","core/templates","tool_lp/tree","tool_lp/competency_outcomes","jquery"],function(a,b,c,d,e,f){var g={},h=0,i="",j="",k="",l=!1,m=function(a,b){var c=0,d=!1;for(a.haschildren=!1,a.children=[],c=0;c0&&(k=g),this.on("selectionchanged",o)},on:function(a,b){f(j).on(a,b)},getChildren:function(a){var b=[];return f.each(g,function(c,d){d.parentid==a&&b.push(d)}),b},getCompetencyFrameworkId:function(){return h},getCompetency:function(a){return g[a]},getCompetencyLevel:function(a){var b=this.getCompetency(a),c=b.path.replace(/^\/|\/$/g,"").split("/").length;return c},hasChildren:function(a){return this.getChildren(a).length>0},hasRule:function(a){var b=this.getCompetency(a);return!!b&&(b.ruleoutcome!=e.OUTCOME_NONE&&b.ruletype)},reloadCompetencies:function(){return n("").fail(b.exception)},listCompetencies:function(){return g}}}); \ No newline at end of file diff --git a/admin/tool/lp/amd/src/competencyactions.js b/admin/tool/lp/amd/src/competencyactions.js index 5c433f4ba7f..60c04daa6d5 100644 --- a/admin/tool/lp/amd/src/competencyactions.js +++ b/admin/tool/lp/amd/src/competencyactions.js @@ -672,6 +672,7 @@ define(['jquery', context.showdeleterelatedaction = true; context.showrelatedcompetencies = true; context.showrule = false; + context.pluginbaseurl = url.relativeUrl('/admin/tool/lp'); if (competency.ruleoutcome != Outcomes.NONE) { // Get the outcome and rule name. diff --git a/admin/tool/lp/amd/src/competencytree.js b/admin/tool/lp/amd/src/competencytree.js index f912b5936cd..d56efa7bbc7 100644 --- a/admin/tool/lp/amd/src/competencytree.js +++ b/admin/tool/lp/amd/src/competencytree.js @@ -136,18 +136,22 @@ define(['core/ajax', 'core/notification', 'core/templates', 'tool_lp/tree', 'too /** * Initialise the tree. * - * @param {Number} id The competency id. + * @param {Number} id The competency framework id. * @param {String} shortname The framework shortname * @param {String} search The current search string * @param {String} selector The selector for the tree div * @param {Boolean} canmanage Can manage the competencies + * @param {Number} competencyid */ - init: function(id, shortname, search, selector, canmanage) { + init: function(id, shortname, search, selector, canmanage, competencyid) { competencyFrameworkId = id; competencyFrameworkShortName = shortname; competencyFramworkCanManage = canmanage; treeSelector = selector; loadCompetencies(search).fail(notification.exception); + if (competencyid > 0) { + currentNodeId = competencyid; + } this.on('selectionchanged', rememberCurrent); }, diff --git a/admin/tool/lp/classes/external/competency_summary_exporter.php b/admin/tool/lp/classes/external/competency_summary_exporter.php index af85059422f..4ab1a954c31 100644 --- a/admin/tool/lp/classes/external/competency_summary_exporter.php +++ b/admin/tool/lp/classes/external/competency_summary_exporter.php @@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die(); use context_course; use renderer_base; use stdClass; +use moodle_url; use core_competency\competency_framework; use core_competency\external\competency_exporter; use core_competency\external\competency_framework_exporter; @@ -82,7 +83,10 @@ class competency_summary_exporter extends \core\external\exporter { ), 'comppath' => array( 'type' => competency_path_exporter::read_properties_definition(), - ) + ), + 'pluginbaseurl' => [ + 'type' => PARAM_URL + ] ); } @@ -136,6 +140,7 @@ class competency_summary_exporter extends \core\external\exporter { 'context' => $context ]); $result->comppath = $exporter->export($output); + $result->pluginbaseurl = (new moodle_url('/admin/tool/lp'))->out(true); return (array) $result; } diff --git a/admin/tool/lp/classes/output/manage_competencies_page.php b/admin/tool/lp/classes/output/manage_competencies_page.php index c7785341f3c..07c3bfb2ed8 100644 --- a/admin/tool/lp/classes/output/manage_competencies_page.php +++ b/admin/tool/lp/classes/output/manage_competencies_page.php @@ -69,10 +69,11 @@ class manage_competencies_page implements renderable, templatable { * @param string $search Search string. * @param context $pagecontext The page context. */ - public function __construct($framework, $search, $pagecontext) { + public function __construct($framework, $search, $pagecontext, $competency) { $this->framework = $framework; $this->pagecontext = $pagecontext; $this->search = $search; + $this->competency = $competency; $addpage = new single_button( new moodle_url('/admin/tool/lp/editcompetencyframework.php'), get_string('addnewcompetency', 'tool_lp') @@ -97,6 +98,11 @@ class manage_competencies_page implements renderable, templatable { $data->pagecontextid = $this->pagecontext->id; $data->pluginbaseurl = (new moodle_url('/admin/tool/lp'))->out(true); + $competencyid = 0; + if ($this->competency) { + $data->competencyid = $this->competency->get('id'); + } + $rulesmodules = array(); $rules = competency::get_available_rules(); foreach ($rules as $type => $rulename) { diff --git a/admin/tool/lp/competencies.php b/admin/tool/lp/competencies.php index facae66724f..3543c181b90 100644 --- a/admin/tool/lp/competencies.php +++ b/admin/tool/lp/competencies.php @@ -25,14 +25,23 @@ require_once(__DIR__ . '/../../../config.php'); require_once($CFG->libdir.'/adminlib.php'); -$pagecontextid = required_param('pagecontextid', PARAM_INT); // Reference to the context we came from. $search = optional_param('search', '', PARAM_RAW); -$id = required_param('competencyframeworkid', PARAM_INT); +$competencyid = optional_param('competencyid', 0, PARAM_INT); +$competency = null; +if ($competencyid) { + $competency = \core_competency\api::read_competency($competencyid); + $id = $competency->get('competencyframeworkid'); + $pagecontext = $competency->get_context(); + $pagecontextid = $pagecontext->id; // Reference to the context we came from. +} else { + $id = required_param('competencyframeworkid', PARAM_INT); + $pagecontextid = required_param('pagecontextid', PARAM_INT); // Reference to the context we came from. + $pagecontext = context::instance_by_id($pagecontextid); +} require_login(); \core_competency\api::require_enabled(); -$pagecontext = context::instance_by_id($pagecontextid); $framework = \core_competency\api::read_framework($id); $context = $framework->get_context(); @@ -58,7 +67,7 @@ $PAGE->set_heading($title); $output = $PAGE->get_renderer('tool_lp'); echo $output->header(); -$page = new \tool_lp\output\manage_competencies_page($framework, $search, $pagecontext); +$page = new \tool_lp\output\manage_competencies_page($framework, $search, $pagecontext, $competency); echo $output->render($page); // Log the framework viewed event after rendering the page. diff --git a/admin/tool/lp/templates/competency_path.mustache b/admin/tool/lp/templates/competency_path.mustache index 96afe156dcf..63dfa451d7f 100644 --- a/admin/tool/lp/templates/competency_path.mustache +++ b/admin/tool/lp/templates/competency_path.mustache @@ -59,7 +59,7 @@ {{{framework.name}}} / {{#ancestors}} - {{{name}}} + {{{name}}} {{^last}} / {{/last}} {{/ancestors}} diff --git a/admin/tool/lp/templates/competency_summary.mustache b/admin/tool/lp/templates/competency_summary.mustache index 1ce22b16c64..85acda08503 100644 --- a/admin/tool/lp/templates/competency_summary.mustache +++ b/admin/tool/lp/templates/competency_summary.mustache @@ -50,7 +50,7 @@ }}
{{#framework}}
diff --git a/admin/tool/lp/templates/manage_competencies_page.mustache b/admin/tool/lp/templates/manage_competencies_page.mustache index 9f5c317af0e..efb82823e87 100644 --- a/admin/tool/lp/templates/manage_competencies_page.mustache +++ b/admin/tool/lp/templates/manage_competencies_page.mustache @@ -135,7 +135,8 @@ require(['tool_lp/tree', 'tool_lp/competencytree', 'tool_lp/competencyactions', {{#quote}} {{{framework.shortname}}} {{/quote}}, '{{search}}', '[data-enhance=tree]', - {{canmanage}}); + {{canmanage}}, + {{competencyid}}); actions.init(treeModel, {{pagecontextid}}, '{{{framework.taxonomies}}}', {{{rulesmodules}}}); From 43f1c8e23c239c307f568e4e3d5a5e789ded312e Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Tue, 19 Feb 2019 12:10:58 +0800 Subject: [PATCH 5/8] MDL-63876 badges: Allow criteria to be optional Competencies criteria type is only added if competencies are enabled. --- badges/criteria/award_criteria.php | 36 +++++++--------- badges/criteria/award_criteria_competency.php | 40 +++++++++++++---- lang/en/badges.php | 1 + lib/badgeslib.php | 43 +++++++++++++++++++ 4 files changed, 92 insertions(+), 28 deletions(-) diff --git a/badges/criteria/award_criteria.php b/badges/criteria/award_criteria.php index 6f689d0df4b..4519d706371 100644 --- a/badges/criteria/award_criteria.php +++ b/badges/criteria/award_criteria.php @@ -86,23 +86,6 @@ define('BADGE_CRITERIA_TYPE_COHORT', 8); */ define('BADGE_CRITERIA_TYPE_COMPETENCY', 9); -/* - * Criteria type constant to class name mapping - */ -global $BADGE_CRITERIA_TYPES; -$BADGE_CRITERIA_TYPES = array( - BADGE_CRITERIA_TYPE_OVERALL => 'overall', - BADGE_CRITERIA_TYPE_ACTIVITY => 'activity', - BADGE_CRITERIA_TYPE_MANUAL => 'manual', - BADGE_CRITERIA_TYPE_SOCIAL => 'social', - BADGE_CRITERIA_TYPE_COURSE => 'course', - BADGE_CRITERIA_TYPE_COURSESET => 'courseset', - BADGE_CRITERIA_TYPE_PROFILE => 'profile', - BADGE_CRITERIA_TYPE_BADGE => 'badge', - BADGE_CRITERIA_TYPE_COHORT => 'cohort', - BADGE_CRITERIA_TYPE_COMPETENCY => 'competency', -); - /** * Award criteria abstract definition * @@ -168,13 +151,17 @@ abstract class award_criteria { * @return award_criteria */ public static function build($params) { - global $CFG, $BADGE_CRITERIA_TYPES; + global $CFG; - if (!isset($params['criteriatype']) || !isset($BADGE_CRITERIA_TYPES[$params['criteriatype']])) { + require_once($CFG->libdir . '/badgeslib.php'); + + $types = badges_list_criteria(false); + + if (!isset($params['criteriatype']) || !isset($types[$params['criteriatype']])) { print_error('error:invalidcriteriatype', 'badges'); } - $class = 'award_criteria_' . $BADGE_CRITERIA_TYPES[$params['criteriatype']]; + $class = 'award_criteria_' . $types[$params['criteriatype']]; require_once($CFG->dirroot . '/badges/criteria/' . $class . '.php'); return new $class($params); @@ -501,4 +488,13 @@ abstract class award_criteria { } } } + + /** + * Allow some specific criteria types to be disabled based on config. + * + * @return boolean + */ + public static function is_enabled() { + return true; + } } diff --git a/badges/criteria/award_criteria_competency.php b/badges/criteria/award_criteria_competency.php index a41810bc056..2a3cd25446c 100644 --- a/badges/criteria/award_criteria_competency.php +++ b/badges/criteria/award_criteria_competency.php @@ -37,6 +37,9 @@ class award_criteria_competency extends award_criteria { /* @var int Criteria [BADGE_CRITERIA_TYPE_COMPETENCY] */ public $criteriatype = BADGE_CRITERIA_TYPE_COMPETENCY; + public $required_param = 'competency'; + public $optional_params = []; + public $required_param = 'competency'; public $self_validation = true; @@ -56,10 +59,14 @@ class award_criteria_competency extends award_criteria { if ($short) { $competency->set('description', ''); } - if ($pluginsfunction = get_plugins_with_function('render_competency_summary')) { - foreach ($pluginsfunction as $plugintype => $plugins) { - foreach ($plugins as $pluginfunction) { - $output[] = $pluginfunction($competency, $competency->get_framework(), !$short, !$short); + if (!self::is_enabled()) { + $output[] = get_string('competenciesarenotenabled', 'core_competency'); + } else { + if ($pluginsfunction = get_plugins_with_function('render_competency_summary')) { + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $output[] = $pluginfunction($competency, $competency->get_framework(), !$short, !$short); + } } } } @@ -101,11 +108,12 @@ class award_criteria_competency extends award_criteria { if ($pluginsfunction = get_plugins_with_function('competency_picker')) { foreach ($pluginsfunction as $plugintype => $plugins) { foreach ($plugins as $pluginfunction) { - $output[] = $pluginfunction($mform, $courseid, $context, 'competency'); + $output[] = $pluginfunction($mform, $courseid, $context, 'competency_competencies'); } } } - $mform->getElement('competency')->setValue($competencies); + $mform->getElement('competency_competencies')->setValue($competencies); + $mform->addRule('competency_competencies', get_string('requiredcompetency', 'badges'), 'required'); // Add aggregation. if (!$none) { @@ -130,8 +138,8 @@ class award_criteria_competency extends award_criteria { * @param array $params Values from the form or any other array. */ public function save($params = array()) { - $competencies = $params['competency']; - unset($params['competency']); + $competencies = $params['competency_competencies']; + unset($params['competency_competencies']); if (is_string($competencies)) { $competencies = explode(',', $competencies); } @@ -156,6 +164,9 @@ class award_criteria_competency extends award_criteria { $overall = false; $competencyids = []; + if (!self::is_enabled()) { + return false; + } foreach ($this->params as $param) { $competencyids[] = $param['competency']; } @@ -208,6 +219,10 @@ class award_criteria_competency extends award_criteria { $badge = $DB->get_record('badge', array('id' => $this->badgeid)); + if (!self::is_enabled()) { + return array($join, $where, $params); + } + if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) { // User has received ANY of the required competencies (we can use an in or equals list). foreach ($this->params as $param) { @@ -245,4 +260,13 @@ class award_criteria_competency extends award_criteria { } return array($join, $where, $params); } + + /** + * Hide this criteria when competencies are disabled. + * + * @return boolean + */ + public static function is_enabled() { + return \core_competency\api::is_enabled(); + } } diff --git a/lang/en/badges.php b/lang/en/badges.php index a959a438f4e..8e1c503620e 100644 --- a/lang/en/badges.php +++ b/lang/en/badges.php @@ -467,6 +467,7 @@ $string['relative'] = 'Relative date'; $string['relatedbages'] = 'Related badges'; $string['revoke'] = 'Revoke badge'; $string['requiredcohort'] = 'At least one cohort should be added to the cohort criterion.'; +$string['requiredcompetency'] = 'At least one competency should be added to the competency criterion.'; $string['requiredcourse'] = 'At least one course should be added to the courseset criterion.'; $string['requiredbadge'] = 'At least one badge should be added to the badge criterion.'; $string['reviewbadge'] = 'Changes in badge access'; diff --git a/lib/badgeslib.php b/lib/badgeslib.php index a95d2371b39..8d4ad138af3 100644 --- a/lib/badgeslib.php +++ b/lib/badgeslib.php @@ -218,6 +218,7 @@ class badge { * @return array */ public function get_accepted_criteria() { + global $CFG; $criteriatypes = array(); if ($this->type == BADGE_TYPE_COURSE) { @@ -240,6 +241,12 @@ class badge { BADGE_CRITERIA_TYPE_COMPETENCY ); } + $alltypes = badges_list_criteria(); + foreach ($criteriatypes as $index => $type) { + if (!isset($alltypes[$type])) { + unset($criteriatypes[$index]); + } + } return $criteriatypes; } @@ -1535,3 +1542,39 @@ function badges_setup_backpack_js() { $PAGE->requires->js('/badges/backpack.js', true); } } + +/** + * Return all the enabled criteria types for this site. + * + * @return array + */ +function badges_list_criteria($enabled = true) { + global $CFG; + + $types = array( + BADGE_CRITERIA_TYPE_OVERALL => 'overall', + BADGE_CRITERIA_TYPE_ACTIVITY => 'activity', + BADGE_CRITERIA_TYPE_MANUAL => 'manual', + BADGE_CRITERIA_TYPE_SOCIAL => 'social', + BADGE_CRITERIA_TYPE_COURSE => 'course', + BADGE_CRITERIA_TYPE_COURSESET => 'courseset', + BADGE_CRITERIA_TYPE_PROFILE => 'profile', + BADGE_CRITERIA_TYPE_BADGE => 'badge', + BADGE_CRITERIA_TYPE_COHORT => 'cohort', + BADGE_CRITERIA_TYPE_COMPETENCY => 'competency', + ); + if ($enabled) { + foreach ($types as $key => $type) { + $class = 'award_criteria_' . $type; + $file = $CFG->dirroot . '/badges/criteria/' . $class . '.php'; + if (file_exists($file)) { + require_once($file); + + if (!$class::is_enabled()) { + unset($types[$key]); + } + } + } + } + return $types; +} From 8aff6f6f14f9958495696d2970412642ec71e85a Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Tue, 19 Feb 2019 12:49:01 +0800 Subject: [PATCH 6/8] MDL-63876 badges: Coding style Add comments to document function params and template example context. --- admin/tool/lp/amd/src/competencytree.js | 2 +- .../lp/amd/src/form_competency_element.js | 3 ++ .../output/manage_competencies_page.php | 6 ++- .../site_competencies_form_element.php | 37 +++++++++---------- admin/tool/lp/lib.php | 7 ++++ .../manage_competencies_page.mustache | 15 ++++++-- badges/classes/observer.php | 2 - badges/criteria/award_criteria_competency.php | 16 +++----- badges/criteria_form.php | 5 ++- competency/upgrade.txt | 5 +++ lib/badgeslib.php | 13 +++++++ 11 files changed, 72 insertions(+), 39 deletions(-) diff --git a/admin/tool/lp/amd/src/competencytree.js b/admin/tool/lp/amd/src/competencytree.js index d56efa7bbc7..01007e1a3ef 100644 --- a/admin/tool/lp/amd/src/competencytree.js +++ b/admin/tool/lp/amd/src/competencytree.js @@ -141,7 +141,7 @@ define(['core/ajax', 'core/notification', 'core/templates', 'tool_lp/tree', 'too * @param {String} search The current search string * @param {String} selector The selector for the tree div * @param {Boolean} canmanage Can manage the competencies - * @param {Number} competencyid + * @param {Number} competencyid The id of the competency to show first */ init: function(id, shortname, search, selector, canmanage, competencyid) { competencyFrameworkId = id; diff --git a/admin/tool/lp/amd/src/form_competency_element.js b/admin/tool/lp/amd/src/form_competency_element.js index 805e15029f6..d2c813b1873 100644 --- a/admin/tool/lp/amd/src/form_competency_element.js +++ b/admin/tool/lp/amd/src/form_competency_element.js @@ -32,6 +32,7 @@ define(['jquery', 'tool_lp/competencypicker', 'core/ajax', 'core/notification', * Re-render the list of selected competencies. * * @method renderCompetencies + * @return {boolean} */ var renderCompetencies = function() { var currentCompetencies = $('[data-action="competencies"]').val(); @@ -72,6 +73,8 @@ define(['jquery', 'tool_lp/competencypicker', 'core/ajax', 'core/notification', * Deselect a competency * * @method unpickCompetenciesHandler + * @param {Event} e + * @return {boolean} */ var unpickCompetenciesHandler = function(e) { var currentCompetencies = $('[data-action="competencies"]').val().split(','), diff --git a/admin/tool/lp/classes/output/manage_competencies_page.php b/admin/tool/lp/classes/output/manage_competencies_page.php index 07c3bfb2ed8..c4fbf0400dd 100644 --- a/admin/tool/lp/classes/output/manage_competencies_page.php +++ b/admin/tool/lp/classes/output/manage_competencies_page.php @@ -62,12 +62,16 @@ class manage_competencies_page implements renderable, templatable { /** @var context $pagecontext The page context. */ protected $pagecontext = null; + /** @var \core_competency\competency $competency The competency to show when the page loads. */ + protected $competency = null; + /** * Construct this renderable. * * @param \core_competency\competency_framework $framework Competency framework. * @param string $search Search string. * @param context $pagecontext The page context. + * @param \core_competency\competency $competency The core competency to show when the page loads. */ public function __construct($framework, $search, $pagecontext, $competency) { $this->framework = $framework; @@ -98,7 +102,7 @@ class manage_competencies_page implements renderable, templatable { $data->pagecontextid = $this->pagecontext->id; $data->pluginbaseurl = (new moodle_url('/admin/tool/lp'))->out(true); - $competencyid = 0; + $data->competencyid = 0; if ($this->competency) { $data->competencyid = $this->competency->get('id'); } diff --git a/admin/tool/lp/classes/site_competencies_form_element.php b/admin/tool/lp/classes/site_competencies_form_element.php index 270d8a221b4..02050d16257 100644 --- a/admin/tool/lp/classes/site_competencies_form_element.php +++ b/admin/tool/lp/classes/site_competencies_form_element.php @@ -16,10 +16,10 @@ /** - * Course competencies element. + * Site competencies element. * * @package tool_lp - * @copyright 2016 Damyon Wiese + * @copyright 2019 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -30,10 +30,10 @@ global $CFG; require_once($CFG->libdir . '/form/hidden.php'); /** - * Course competencies element. + * Site competencies element. * * @package tool_lp - * @copyright 2016 Damyon Wiese + * @copyright 2019 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class tool_lp_site_competencies_form_element extends MoodleQuickForm_hidden { @@ -41,28 +41,25 @@ class tool_lp_site_competencies_form_element extends MoodleQuickForm_hidden { /** * Constructor * - * @param string $elementName Element name - * @param mixed $elementLabel Label(s) for an element - * @param array $options Options to control the element's display + * @param string $elementname Element name. + * @param string $value The element value. * @param mixed $attributes Either a typical HTML attribute string or an associative array. */ - public function __construct($elementName=null, $value='', $attributes=null) { - global $OUTPUT; - - if ($elementName == null) { + public function __construct($elementname=null, $value='', $attributes=null) { + if ($elementname == null) { // This is broken quickforms messing with the constructors. return; } - $attributes = array_merge(['data-action' => 'competencies'], $attributes?$attributes:[]); + $attributes = array_merge(['data-action' => 'competencies'], $attributes ? $attributes : []); - parent::__construct($elementName, $value, $attributes); + parent::__construct($elementname, $value, $attributes); $this->setType('hidden'); } /** * Generate the hidden field and the controls to show and pick the competencies. */ - function toHtml(){ + public function toHtml() { global $PAGE; $html = parent::toHTML(); @@ -77,7 +74,9 @@ class tool_lp_site_competencies_form_element extends MoodleQuickForm_hidden { $html .= '
'; $html .= '
'; $html .= ''; $html .= '
'; $html .= '
'; @@ -88,12 +87,12 @@ class tool_lp_site_competencies_form_element extends MoodleQuickForm_hidden { /** * Accepts a renderer * - * @param object An HTML_QuickForm_Renderer object - * @access public + * @param HTML_QuickForm_Renderer $renderer the renderer for the element. + * @param boolean $required not used. + * @param string $error not used. * @return void */ - function accept(&$renderer, $required=false, $error=null) - { + public function accept(&$renderer, $required=false, $error=null) { $renderer->renderElement($this, false, ''); } } diff --git a/admin/tool/lp/lib.php b/admin/tool/lp/lib.php index 55130165e1d..c4278372c7f 100644 --- a/admin/tool/lp/lib.php +++ b/admin/tool/lp/lib.php @@ -220,6 +220,13 @@ function tool_lp_get_fontawesome_icon_map() { /** * Render a short bit of information about a competency. + * + * @param \core_competency\competency $competency The competency to show. + * @param \core_competency\competency_framework $framework The competency framework. + * @param boolean $includerelated If true, show related competencies. + * @param boolean $includecourses If true, show courses using this competency. + * @param boolean $skipenabled If true, show this competency even if competencies are disabled. + * @return string The html summary for the competency. */ function tool_lp_render_competency_summary(\core_competency\competency $competency, \core_competency\competency_framework $framework, diff --git a/admin/tool/lp/templates/manage_competencies_page.mustache b/admin/tool/lp/templates/manage_competencies_page.mustache index efb82823e87..75f9086b723 100644 --- a/admin/tool/lp/templates/manage_competencies_page.mustache +++ b/admin/tool/lp/templates/manage_competencies_page.mustache @@ -15,6 +15,8 @@ along with Moodle. If not, see . }} {{! + @template tool_lp/manage_competencies_page + Manage competencies template. Classes required for JS: @@ -25,10 +27,15 @@ * data-region=term * data-region=selected-competency - Context variables required for this template: - * framework - - * competencies - array of objects containing id, shortname, idnumber, sortorder, parentid, competencyframeworkid, path - * canmanage - true if this user has permission to manage the competencies + Example context (json): + { + "framework": { + "description": "Framework", + "id": 0 + }, + "competencies": [], + "canmanage": false + } }}

diff --git a/badges/classes/observer.php b/badges/classes/observer.php index f33a7f01c4d..9452e912d2a 100644 --- a/badges/classes/observer.php +++ b/badges/classes/observer.php @@ -86,7 +86,6 @@ class core_badges_observer { $ucid = $event->other['usercompetencyid']; $cid = $event->other['competencyid']; - $eventdata = $event->get_record_snapshot('competency_usercomp', $ucid); $userid = $event->relateduserid; if ($rs = $DB->get_records('badge_criteria_param', array('name' => 'competency_' . $cid, 'value' => $cid))) { @@ -122,7 +121,6 @@ class core_badges_observer { if (!empty($CFG->enablebadges)) { require_once($CFG->dirroot.'/lib/badgeslib.php'); - $eventdata = $event->get_record_snapshot('course_completions', $event->objectid); $userid = $event->relateduserid; $courseid = $event->courseid; diff --git a/badges/criteria/award_criteria_competency.php b/badges/criteria/award_criteria_competency.php index 2a3cd25446c..416b928b2b1 100644 --- a/badges/criteria/award_criteria_competency.php +++ b/badges/criteria/award_criteria_competency.php @@ -35,23 +35,20 @@ defined('MOODLE_INTERNAL') || die(); */ class award_criteria_competency extends award_criteria { - /* @var int Criteria [BADGE_CRITERIA_TYPE_COMPETENCY] */ + /* @var int The criteria type */ public $criteriatype = BADGE_CRITERIA_TYPE_COMPETENCY; + /* @var string a required param */ public $required_param = 'competency'; + /* @var array no optional params */ public $optional_params = []; - public $required_param = 'competency'; - public $self_validation = true; - public $optional_params = array(''); - /** * Get criteria details for displaying to users * @param string $short Print short version of criteria * @return string */ public function get_details($short = '') { - global $DB, $OUTPUT; $output = array(); foreach ($this->params as $p) { @@ -80,9 +77,10 @@ class award_criteria_competency extends award_criteria { /** * Add appropriate new criteria options to the form * @param object $mform moodle form + * @return array First item is a boolean to indicate an error and the second is the error message. */ public function get_options(&$mform) { - global $DB, $PAGE; + global $DB; $none = false; $availablebadges = null; @@ -180,11 +178,9 @@ class award_criteria_competency extends award_criteria { } foreach ($this->params as $param) { - $found = false; $proficiency = false; foreach ($existing as $usercompetency) { if ($usercompetency->get('competencyid') == $param['competency']) { - $found = true; $proficiency = $usercompetency->get('proficiency'); } } @@ -240,7 +236,6 @@ class award_criteria_competency extends award_criteria { } $where .= ' AND uc2.proficiency = :isproficient '; $params['isproficient'] = true; - return array($join, $where, $params); } else { // User has received ALL of the required competencies (we have to join on each one). @@ -256,7 +251,6 @@ class award_criteria_competency extends award_criteria { $params['isproficient' . $joincount] = true; } - return array($join, $where, $params); } return array($join, $where, $params); } diff --git a/badges/criteria_form.php b/badges/criteria_form.php index efd08ecff4f..d9b7377b22a 100644 --- a/badges/criteria_form.php +++ b/badges/criteria_form.php @@ -78,7 +78,10 @@ class edit_criteria_form extends moodleform { $errors = parent::validation($data, $files); $addcourse = $this->_customdata['addcourse']; - if (!$addcourse && isset($this->_customdata['criteria']->required_param) && !isset($this->_customdata['criteria']->self_validation)) { + if (!$addcourse && + isset($this->_customdata['criteria']->required_param) && + !isset($this->_customdata['criteria']->self_validation)) { + $required = $this->_customdata['criteria']->required_param; $pattern1 = '/^' . $required . '_(\d+)$/'; $pattern2 = '/^' . $required . '_(\w+)$/'; diff --git a/competency/upgrade.txt b/competency/upgrade.txt index e9eff07b8ef..5582241b39c 100644 --- a/competency/upgrade.txt +++ b/competency/upgrade.txt @@ -1,6 +1,11 @@ This files describes API changes in /competency/*. The information provided here is intended especially for developers. +=== 3.7 === +* tool_lp can render the ui for a competency summary or a competency picker. + Use get_plugins_with_function('competency_picker') or + get_plugins_with_function('render_competency_summary') to call it. + === 3.3 === * Deprecated classes and their new equivalent: diff --git a/lib/badgeslib.php b/lib/badgeslib.php index 8d4ad138af3..20d25d1d7df 100644 --- a/lib/badgeslib.php +++ b/lib/badgeslib.php @@ -853,6 +853,18 @@ class badge { return $DB->get_records('badge_alignment', array('badgeid' => $this->id)); } + /** + * Get alignments of badge. + * + * @deprecated since Moodle 3.7 see MDL-63876 + * @return array List content alignments. + */ + public function get_alignment() { + debugging('Use of get_alignment is deprecated. Call get_alignments instead.', DEBUG_DEVELOPER); + + return $this->get_alignments(); + } + /** * Insert/update Endorsement information of badge. * @@ -1546,6 +1558,7 @@ function badges_setup_backpack_js() { /** * Return all the enabled criteria types for this site. * + * @param boolean $enabled * @return array */ function badges_list_criteria($enabled = true) { From 6bdaf20462185278d76abe8448feddaa899a69f4 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Fri, 15 Mar 2019 16:06:32 +0800 Subject: [PATCH 7/8] MDL-63876 competencies: Skip enabled check Allow rendering a competency summary if competencies are disabled, but do not include links to competencies pages. --- .../external/competency_path_exporter.php | 4 +++ .../external/competency_summary_exporter.php | 1 + admin/tool/lp/lib.php | 5 ++-- .../lp/templates/competency_path.mustache | 18 ++++++++++-- .../lp/templates/competency_summary.mustache | 9 +++++- badges/criteria/award_criteria_competency.php | 15 +++++----- competency/classes/api.php | 28 ++++++++++++++++++- .../competency_framework_exporter.php | 8 +++++- 8 files changed, 73 insertions(+), 15 deletions(-) diff --git a/admin/tool/lp/classes/external/competency_path_exporter.php b/admin/tool/lp/classes/external/competency_path_exporter.php index 1dfe496ed76..ec83cdc491b 100644 --- a/admin/tool/lp/classes/external/competency_path_exporter.php +++ b/admin/tool/lp/classes/external/competency_path_exporter.php @@ -76,6 +76,9 @@ class competency_path_exporter extends \core\external\exporter { ], 'pagecontextid' => [ 'type' => PARAM_INT + ], + 'showlinks' => [ + 'type' => PARAM_BOOL ] ]; } @@ -91,6 +94,7 @@ class competency_path_exporter extends \core\external\exporter { $ancestors = []; $nodescount = count($this->related['ancestors']); $i = 1; + $result->showlinks = \core_competency\api::show_links(); foreach ($this->related['ancestors'] as $competency) { $exporter = new path_node_exporter([ 'id' => $competency->get('id'), diff --git a/admin/tool/lp/classes/external/competency_summary_exporter.php b/admin/tool/lp/classes/external/competency_summary_exporter.php index 4ab1a954c31..d007a8c084c 100644 --- a/admin/tool/lp/classes/external/competency_summary_exporter.php +++ b/admin/tool/lp/classes/external/competency_summary_exporter.php @@ -141,6 +141,7 @@ class competency_summary_exporter extends \core\external\exporter { ]); $result->comppath = $exporter->export($output); $result->pluginbaseurl = (new moodle_url('/admin/tool/lp'))->out(true); + $result->showlinks = \core_competency\api::show_links(); return (array) $result; } diff --git a/admin/tool/lp/lib.php b/admin/tool/lp/lib.php index c4278372c7f..e17c031e1c3 100644 --- a/admin/tool/lp/lib.php +++ b/admin/tool/lp/lib.php @@ -231,10 +231,11 @@ function tool_lp_get_fontawesome_icon_map() { function tool_lp_render_competency_summary(\core_competency\competency $competency, \core_competency\competency_framework $framework, $includerelated, - $includecourses) { + $includecourses, + $skipenabled = false) { global $PAGE; - if (!get_config('core_competency', 'enabled')) { + if (!$skipenabled && !get_config('core_competency', 'enabled')) { return; } diff --git a/admin/tool/lp/templates/competency_path.mustache b/admin/tool/lp/templates/competency_path.mustache index 63dfa451d7f..c3816c49fa2 100644 --- a/admin/tool/lp/templates/competency_path.mustache +++ b/admin/tool/lp/templates/competency_path.mustache @@ -56,10 +56,24 @@ }}