From d4c0a2f66ef99e98a989f77bf4d237982deafe24 Mon Sep 17 00:00:00 2001 From: Serge Gauthier Date: Wed, 2 Dec 2015 15:06:52 -0500 Subject: [PATCH] MDL-51039 tool_lp: Add completion rules to course - competency links --- admin/tool/lp/amd/build/competencies.min.js | 2 +- admin/tool/lp/amd/src/competencies.js | 22 ++++ admin/tool/lp/classes/api.php | 102 ++++++++++++++--- admin/tool/lp/classes/course_competency.php | 105 +++++++++++++++++- admin/tool/lp/classes/external.php | 100 ++++++++++++++--- .../output/course_competencies_page.php | 27 ++++- admin/tool/lp/db/install.xml | 4 + admin/tool/lp/db/services.php | 13 ++- admin/tool/lp/db/upgrade.php | 21 ++++ admin/tool/lp/lang/en/tool_lp.php | 5 + .../course_competencies_page.mustache | 8 ++ admin/tool/lp/tests/api_test.php | 27 +++++ admin/tool/lp/version.php | 3 +- 13 files changed, 393 insertions(+), 46 deletions(-) diff --git a/admin/tool/lp/amd/build/competencies.min.js b/admin/tool/lp/amd/build/competencies.min.js index d87e1cc80a4..799a376cd1d 100644 --- a/admin/tool/lp/amd/build/competencies.min.js +++ b/admin/tool/lp/amd/build/competencies.min.js @@ -1 +1 @@ -define(["jquery","core/notification","core/ajax","core/templates","core/str","tool_lp/competencypicker","tool_lp/dragdrop-reorder"],function(a,b,c,d,e,f,g){var h=function(b,c,d){this.itemid=b,this.itemtype=c,this.pageContextId=d,this.pickerInstance=null,a('[data-region="actions"] button').show(),this.registerEvents(),this.registerDragDrop()};return h.prototype.registerDragDrop=function(){var a=this;e.get_string("movecompetency","tool_lp").done(function(b){g.dragdrop("movecompetency",b,{identifier:"movecompetency",component:"tool_lp"},{identifier:"movecompetencyafter",component:"tool_lp"},"drag-samenode","drag-parentnode","drag-handlecontainer",function(b,c){a.handleDrop.call(a,b,c)})}).fail(b.exception)},h.prototype.handleDrop=function(d,e){var f=a(d).data("id"),g=a(e).data("id"),h=this,i=[];if("course"==h.itemtype)i=c.call([{methodname:"tool_lp_reorder_course_competency",args:{courseid:h.itemid,competencyidfrom:f,competencyidto:g}}]);else if("template"==h.itemtype)i=c.call([{methodname:"tool_lp_reorder_template_competency",args:{templateid:h.itemid,competencyidfrom:f,competencyidto:g}}]);else{if("plan"!=h.itemtype)return null;i=c.call([{methodname:"tool_lp_reorder_plan_competency",args:{planid:h.itemid,competencyidfrom:f,competencyidto:g}}])}i[0].fail(b.exception)},h.prototype.pickCompetency=function(){var e,g,h,i=this;i.pickerInstance||(i.pickerInstance=new f(i.pageContextId,void 0,"course"===i.itemtype?"parents":void 0),i.pickerInstance.on("save",function(f,j){var k=j.competencyIds;"course"===i.itemtype?(e=[],a.each(k,function(a,b){e.push({methodname:"tool_lp_add_competency_to_course",args:{courseid:i.itemid,competencyid:b}})}),e.push({methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:i.itemid}}),g="tool_lp/course_competencies_page",h="coursecompetenciespage"):"template"===i.itemtype?(e=[],a.each(k,function(a,b){e.push({methodname:"tool_lp_add_competency_to_template",args:{templateid:i.itemid,competencyid:b}})}),e.push({methodname:"tool_lp_data_for_template_competencies_page",args:{templateid:i.itemid,pagecontext:{contextid:i.pageContextId}}}),g="tool_lp/template_competencies_page",h="templatecompetenciespage"):"plan"===i.itemtype&&(e=[],a.each(k,function(a,b){e.push({methodname:"tool_lp_add_competency_to_plan",args:{planid:i.itemid,competencyid:b}})}),e.push({methodname:"tool_lp_data_for_plan_competencies_page",args:{planid:i.itemid}}),g="tool_lp/plan_page",h="plancompetenciespage"),c.call(e)[e.length-1].then(function(b){return d.render(g,b).done(function(b,c){a('[data-region="'+h+'"]').replaceWith(b),d.runTemplateJS(c)})},b.exception)})),i.pickerInstance.display()},h.prototype.registerEvents=function(){var e=this;a('[data-region="actions"] button').click(function(a){a.preventDefault(),e.pickCompetency()}),a('[data-action="delete-competency-link"]').click(function(f){var g=[],h="",i="";f.preventDefault();var j=a(f.target).closest("[data-id]").data("id");"course"==e.itemtype?(g=c.call([{methodname:"tool_lp_remove_competency_from_course",args:{courseid:e.itemid,competencyid:j}},{methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:e.itemid}}]),h="tool_lp/course_competencies_page",i="coursecompetenciespage"):"template"==e.itemtype?(g=c.call([{methodname:"tool_lp_remove_competency_from_template",args:{templateid:e.itemid,competencyid:j}},{methodname:"tool_lp_data_for_template_competencies_page",args:{templateid:e.itemid,pagecontext:{contextid:e.pageContextId}}}]),h="tool_lp/template_competencies_page",i="templatecompetenciespage"):"plan"==e.itemtype&&(g=c.call([{methodname:"tool_lp_remove_competency_from_plan",args:{planid:e.itemid,competencyid:j}},{methodname:"tool_lp_data_for_plan_competencies_page",args:{planid:e.itemid}}]),h="tool_lp/plan_page",i="plancompetenciespage"),g[1].done(function(c){d.render(h,c).done(function(b,c){a('[data-region="'+i+'"]').replaceWith(b),d.runTemplateJS(c)}).fail(b.exception)}).fail(b.exception)})},h}); \ No newline at end of file +define(["jquery","core/notification","core/ajax","core/templates","core/str","tool_lp/competencypicker","tool_lp/dragdrop-reorder"],function(a,b,c,d,e,f,g){var h=function(b,c,d){this.itemid=b,this.itemtype=c,this.pageContextId=d,this.pickerInstance=null,a('[data-region="actions"] button').show(),this.registerEvents(),this.registerDragDrop()};return h.prototype.registerDragDrop=function(){var a=this;e.get_string("movecompetency","tool_lp").done(function(b){g.dragdrop("movecompetency",b,{identifier:"movecompetency",component:"tool_lp"},{identifier:"movecompetencyafter",component:"tool_lp"},"drag-samenode","drag-parentnode","drag-handlecontainer",function(b,c){a.handleDrop.call(a,b,c)})}).fail(b.exception)},h.prototype.handleDrop=function(d,e){var f=a(d).data("id"),g=a(e).data("id"),h=this,i=[];if("course"==h.itemtype)i=c.call([{methodname:"tool_lp_reorder_course_competency",args:{courseid:h.itemid,competencyidfrom:f,competencyidto:g}}]);else if("template"==h.itemtype)i=c.call([{methodname:"tool_lp_reorder_template_competency",args:{templateid:h.itemid,competencyidfrom:f,competencyidto:g}}]);else{if("plan"!=h.itemtype)return null;i=c.call([{methodname:"tool_lp_reorder_plan_competency",args:{planid:h.itemid,competencyidfrom:f,competencyidto:g}}])}i[0].fail(b.exception)},h.prototype.pickCompetency=function(){var e,g,h,i=this;i.pickerInstance||(i.pickerInstance=new f(i.pageContextId,void 0,"course"===i.itemtype?"parents":void 0),i.pickerInstance.on("save",function(f,j){var k=j.competencyIds;"course"===i.itemtype?(e=[],a.each(k,function(a,b){e.push({methodname:"tool_lp_add_competency_to_course",args:{courseid:i.itemid,competencyid:b}})}),e.push({methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:i.itemid}}),g="tool_lp/course_competencies_page",h="coursecompetenciespage"):"template"===i.itemtype?(e=[],a.each(k,function(a,b){e.push({methodname:"tool_lp_add_competency_to_template",args:{templateid:i.itemid,competencyid:b}})}),e.push({methodname:"tool_lp_data_for_template_competencies_page",args:{templateid:i.itemid,pagecontext:{contextid:i.pageContextId}}}),g="tool_lp/template_competencies_page",h="templatecompetenciespage"):"plan"===i.itemtype&&(e=[],a.each(k,function(a,b){e.push({methodname:"tool_lp_add_competency_to_plan",args:{planid:i.itemid,competencyid:b}})}),e.push({methodname:"tool_lp_data_for_plan_competencies_page",args:{planid:i.itemid}}),g="tool_lp/plan_page",h="plancompetenciespage"),c.call(e)[e.length-1].then(function(b){return d.render(g,b).done(function(b,c){a('[data-region="'+h+'"]').replaceWith(b),d.runTemplateJS(c)})},b.exception)})),i.pickerInstance.display()},h.prototype.registerEvents=function(){var e=this;a('[data-region="coursecompetenciespage"]').on("change",'select[name="ruleoutcome"]',function(f){var g=[],h="tool_lp/course_competencies_page",i="coursecompetenciespage",j=a(f.target).data("id"),k=a(f.target).val();"course"==e.itemtype&&(g=c.call([{methodname:"tool_lp_set_ruleoutcome_course_competency",args:{coursecompetencyid:j,ruleoutcome:k}},{methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:e.itemid}}]),g[1].done(function(c){d.render(h,c).done(function(b,c){a('[data-region="'+i+'"]').replaceWith(b),d.runTemplateJS(c)}).fail(b.exception)}).fail(b.exception))}),a('[data-region="actions"] button').click(function(a){a.preventDefault(),e.pickCompetency()}),a('[data-action="delete-competency-link"]').click(function(f){var g=[],h="",i="";f.preventDefault();var j=a(f.target).closest("[data-id]").data("id");"course"==e.itemtype?(g=c.call([{methodname:"tool_lp_remove_competency_from_course",args:{courseid:e.itemid,competencyid:j}},{methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:e.itemid}}]),h="tool_lp/course_competencies_page",i="coursecompetenciespage"):"template"==e.itemtype?(g=c.call([{methodname:"tool_lp_remove_competency_from_template",args:{templateid:e.itemid,competencyid:j}},{methodname:"tool_lp_data_for_template_competencies_page",args:{templateid:e.itemid,pagecontext:{contextid:e.pageContextId}}}]),h="tool_lp/template_competencies_page",i="templatecompetenciespage"):"plan"==e.itemtype&&(g=c.call([{methodname:"tool_lp_remove_competency_from_plan",args:{planid:e.itemid,competencyid:j}},{methodname:"tool_lp_data_for_plan_competencies_page",args:{planid:e.itemid}}]),h="tool_lp/plan_page",i="plancompetenciespage"),g[1].done(function(c){d.render(h,c).done(function(b,c){a('[data-region="'+i+'"]').replaceWith(b),d.runTemplateJS(c)}).fail(b.exception)}).fail(b.exception)})},h}); \ No newline at end of file diff --git a/admin/tool/lp/amd/src/competencies.js b/admin/tool/lp/amd/src/competencies.js index 1c9a0c6ffaa..992013d1b79 100644 --- a/admin/tool/lp/amd/src/competencies.js +++ b/admin/tool/lp/amd/src/competencies.js @@ -196,6 +196,28 @@ define(['jquery', */ competencies.prototype.registerEvents = function() { var localthis = this; + $('[data-region="coursecompetenciespage"]').on('change', 'select[name="ruleoutcome"]', function(e){ + var requests = []; + var pagerender = 'tool_lp/course_competencies_page'; + var pageregion = 'coursecompetenciespage'; + var coursecompetencyid = $(e.target).data('id') ; + var ruleoutcome = $(e.target).val(); + if (localthis.itemtype == 'course') { + requests = ajax.call([ + { methodname: 'tool_lp_set_ruleoutcome_course_competency', + args: { coursecompetencyid: coursecompetencyid, ruleoutcome: ruleoutcome } }, + { methodname: 'tool_lp_data_for_course_competencies_page', + args: { courseid: localthis.itemid } } + ]); + + requests[1].done(function(context) { + templates.render(pagerender, context).done(function(html, js) { + $('[data-region="' + pageregion + '"]').replaceWith(html); + templates.runTemplateJS(js); + }).fail(notification.exception); + }).fail(notification.exception); + } + }); $('[data-region="actions"] button').click(function(e) { e.preventDefault(); localthis.pickCompetency(); diff --git a/admin/tool/lp/classes/api.php b/admin/tool/lp/classes/api.php index 03086130fda..02085437852 100644 --- a/admin/tool/lp/classes/api.php +++ b/admin/tool/lp/classes/api.php @@ -30,6 +30,7 @@ use context_system; use context_course; use context_user; use coding_exception; +use require_login_exception; use moodle_url; use required_capability_exception; @@ -602,7 +603,7 @@ class api { continue; } - if (!$course->visible && !has_capability('course:viewhidden', $context)) { + if (!$course->visible && !has_capability('course:viewhiddencourses', $context)) { continue; } @@ -632,7 +633,7 @@ class api { unset($courses[$id]); continue; } - if (!$course->visible && !has_capability('course:viewhidden', $context)) { + if (!$course->visible && !has_capability('course:viewhiddencourses', $context)) { unset($courses[$id]); continue; } @@ -649,6 +650,9 @@ class api { * @return int */ public static function count_competencies_in_course($courseid) { + // Check the user have access to the course. + self::validate_course($courseid); + // First we do a permissions check. $context = context_course::instance($courseid); $onlyvisible = 1; @@ -667,27 +671,38 @@ class api { } /** - * List all the competencies in a course. + * List the competencies associated to a course. * - * @param int $courseid The id of the course to check. - * @return array of competencies + * @param mixed $courseorid The course, or its ID. + * @return array( array( + * 'competency' => \tool_lp\competency, + * 'coursecompetency' => \tool_lp\course_competency + * )) */ - public static function list_competencies_in_course($courseid) { - // First we do a permissions check. - $context = context_course::instance($courseid); + public static function list_course_competencies($courseorid) { + $course = $courseorid; + if (!is_object($courseorid)) { + $course = get_course($courseorid); + } + + // Check the user have access to the course. + self::validate_course($course); + $onlyvisible = 1; + $result = array(); + $coursecompetencies = course_competency::list_course_competencies($course->id, $onlyvisible); - $capabilities = array('tool/lp:coursecompetencyread', 'tool/lp:coursecompetencymanage'); - if (!has_any_capability($capabilities, $context)) { - throw new required_capability_exception($context, 'tool/lp:coursecompetencyread', 'nopermissions', ''); + // Build the return values. + foreach ($coursecompetencies as $key => $coursecompetency) { + $competency = new competency($coursecompetency->get_competencyid()); + + $result[] = array( + 'competency' => $competency, + 'coursecompetency' => $coursecompetency + ); } - if (has_capability('tool/lp:coursecompetencymanage', $context)) { - $onlyvisible = 0; - } - - // OK - all set. - return course_competency::list_competencies($courseid, $onlyvisible); + return $result; } /** @@ -698,6 +713,9 @@ class api { * @return bool */ public static function add_competency_to_course($courseid, $competencyid) { + // Check the user have access to the course. + self::validate_course($courseid); + // First we do a permissions check. $context = context_course::instance($courseid); @@ -732,6 +750,9 @@ class api { * @return bool */ public static function remove_competency_from_course($courseid, $competencyid) { + // Check the user have access to the course. + self::validate_course($courseid); + // First we do a permissions check. $context = context_course::instance($courseid); @@ -767,6 +788,9 @@ class api { * @return boolean */ public static function reorder_course_competency($courseid, $competencyidfrom, $competencyidto) { + // Check the user have access to the course. + self::validate_course($courseid); + // First we do a permissions check. $context = context_course::instance($courseid); @@ -2119,4 +2143,48 @@ class api { return $evidence; } + + /** + * Update ruleoutcome value for a course competency. + * + * @param int $coursecompetencyid The course_competency ID. + * @param int $ruleoutcome The value of ruleoutcome. + * @return bool True on success. + */ + public static function set_ruleoutcome_course_competency($coursecompetencyid, $ruleoutcome) { + $coursecompetency = new course_competency($coursecompetencyid); + $coursecontext = context_course::instance($coursecompetency->get_courseid()); + if (!has_capability('tool/lp:coursecompetencymanage', $coursecontext)) { + throw new required_capability_exception($coursecontext, 'tool/lp:coursecompetencymanage', 'nopermissions', ''); + } + $coursecompetency->set_ruleoutcome($ruleoutcome); + + return $coursecompetency->update(); + } + + /** + * Validate if current user have acces to the course if hidden. + * + * @param mixed $courseorid The course or it ID. + * @param bool $throwexception Throw an exception or not. + * @return bool + */ + protected static function validate_course($courseorid, $throwexception = true) { + $course = $courseorid; + if (!is_object($course)) { + $course = get_course($course); + } + + $coursecontext = context_course::instance($course->id); + if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) { + if ($throwexception) { + throw new require_login_exception('Course is hidden'); + } else { + return false; + } + } + + return true; + } + } diff --git a/admin/tool/lp/classes/course_competency.php b/admin/tool/lp/classes/course_competency.php index 4a495723fc6..063d62c6efd 100644 --- a/admin/tool/lp/classes/course_competency.php +++ b/admin/tool/lp/classes/course_competency.php @@ -24,6 +24,7 @@ namespace tool_lp; use stdClass; +use lang_string; /** * Class for loading/storing course_competencies from the DB. @@ -35,6 +36,16 @@ class course_competency extends persistent { const TABLE = 'tool_lp_course_competency'; + /** Course competency ruleoutcome constant. */ + const OUTCOME_NONE = 0; + /** Course competency ruleoutcome constant. */ + const OUTCOME_EVIDENCE = 1; + /** Course competency ruleoutcome constant. */ + const OUTCOME_RECOMMEND = 2; + /** Course competency ruleoutcome constant. */ + const OUTCOME_COMPLETE = 3; + + /** * Return the definition of the properties of this model. * @@ -51,6 +62,15 @@ class course_competency extends persistent { 'sortorder' => array( 'type' => PARAM_INT ), + 'ruleoutcome' => array( + 'choices' => array(self::OUTCOME_NONE, + self::OUTCOME_EVIDENCE, + self::OUTCOME_RECOMMEND, + self::OUTCOME_COMPLETE + ), + 'default' => self::OUTCOME_EVIDENCE, + 'type' => PARAM_INT, + ), ); } @@ -65,6 +85,54 @@ class course_competency extends persistent { } } + /** + * Return a list of rules. + * + * @return array + */ + public static function get_ruleoutcome_list() { + static $list = null; + + if ($list === null) { + $list = array( + self::OUTCOME_NONE => self::get_ruleoutcome_name(self::OUTCOME_NONE), + self::OUTCOME_EVIDENCE => self::get_ruleoutcome_name(self::OUTCOME_EVIDENCE), + self::OUTCOME_RECOMMEND => self::get_ruleoutcome_name(self::OUTCOME_RECOMMEND), + self::OUTCOME_COMPLETE => self::get_ruleoutcome_name(self::OUTCOME_COMPLETE)); + } + + return $list; + } + + /** + * Human readable rule name. + * + * @param int $ruleoutcome The value of ruleoutcome. + * @return string + */ + public static function get_ruleoutcome_name($ruleoutcome) { + + switch ($ruleoutcome) { + case self::OUTCOME_NONE: + $strname = 'none'; + break; + case self::OUTCOME_EVIDENCE: + $strname = 'evidence'; + break; + case self::OUTCOME_RECOMMEND: + $strname = 'recommend'; + break; + case self::OUTCOME_COMPLETE: + $strname = 'complete'; + break; + default: + throw new \moodle_exception('errorcoursecompetencyrule', 'tool_lp', '', $rule); + break; + } + + return new lang_string('coursecompetencyoutcome_' . $strname, 'tool_lp'); + } + /** * Validate course ID. * @@ -169,8 +237,7 @@ class course_competency extends persistent { FROM {' . competency::TABLE . '} comp JOIN {' . self::TABLE . '} coursecomp ON coursecomp.competencyid = comp.id - WHERE coursecomp.courseid = ? - ORDER BY coursecomp.sortorder ASC'; + WHERE coursecomp.courseid = ?'; $params = array($courseid); if ($onlyvisible) { @@ -178,6 +245,7 @@ class course_competency extends persistent { $params[] = 1; } + $sql .= ' ORDER BY coursecomp.sortorder ASC'; $results = $DB->get_recordset_sql($sql, $params); $instances = array(); foreach ($results as $result) { @@ -205,4 +273,37 @@ class course_competency extends persistent { $DB->execute($sql, array($this->get_courseid(), $this->get_sortorder())); } + /** + * List the course_competencies in this course. + * + * @param int $courseid The course id + * @param bool $onlyvisible If true, only count visible competencies in this course. + * @return course_competency[] + */ + public static function list_course_competencies($courseid, $onlyvisible) { + global $DB; + + $sql = 'SELECT coursecomp.* + FROM {' . self::TABLE . '} coursecomp + JOIN {' . competency::TABLE . '} comp + ON coursecomp.competencyid = comp.id + WHERE coursecomp.courseid = ?'; + $params = array($courseid); + + if ($onlyvisible) { + $sql .= ' AND comp.visible = ?'; + $params[] = 1; + } + + $sql .= ' ORDER BY coursecomp.sortorder ASC'; + $results = $DB->get_recordset_sql($sql, $params); + $instances = array(); + foreach ($results as $result) { + array_push($instances, new course_competency(0, $result)); + } + $results->close(); + + return $instances; + } + } diff --git a/admin/tool/lp/classes/external.php b/admin/tool/lp/classes/external.php index f05f3cdadc5..f24d85b1cee 100644 --- a/admin/tool/lp/classes/external.php +++ b/admin/tool/lp/classes/external.php @@ -1338,11 +1338,11 @@ class external extends external_api { } /** - * Returns description of list_competencies_in_course() parameters. + * Returns description of list_course_competencies() parameters. * * @return \external_function_parameters */ - public static function list_competencies_in_course_parameters() { + public static function list_course_competencies_parameters() { $courseid = new external_value( PARAM_INT, 'The course id', @@ -1360,10 +1360,10 @@ class external extends external_api { * @param int $courseid The course id to check. * @return array */ - public static function list_competencies_in_course($courseid) { + public static function list_course_competencies($courseid) { global $PAGE; - $params = self::validate_parameters(self::list_competencies_in_course_parameters(), + $params = self::validate_parameters(self::list_course_competencies_parameters(), array( 'id' => $courseid, )); @@ -1371,29 +1371,42 @@ class external extends external_api { self::validate_context(context_course::instance($params['id'])); $output = $PAGE->get_renderer('tool_lp'); - $competencies = api::list_competencies_in_course($params['id']); - $results = array(); + $competencies = api::list_course_competencies($params['id']); + $result = array(); $contextcache = array(); foreach ($competencies as $competency) { - if (!isset($contextcache[$competency->get_competencyframeworkid()])) { - $contextcache[$competency->get_competencyframeworkid()] = $competency->get_context(); + if (!isset($contextcache[$competency['competency']->get_competencyframeworkid()])) { + $contextcache[$competency['competency']->get_competencyframeworkid()] = $competency['competency']->get_context(); } - $context = $contextcache[$competency->get_competencyframeworkid()]; - $exporter = new competency_exporter($competency, array('context' => $context)); - $record = $exporter->export($output); - array_push($results, $record); + $context = $contextcache[$competency['competency']->get_competencyframeworkid()]; + $exporter = new competency_exporter($competency['competency'], array('context' => $context)); + $competencyrecord = $exporter->export($output); + $exporter = new course_competency_exporter($competency['coursecompetency'], array('context' => $context)); + $coursecompetencyrecord = $exporter->export($output); + $result[] = array( + 'competency' => $competencyrecord, + 'coursecompetency' => $coursecompetencyrecord + ); } - return $results; + + return $result; } /** - * Returns description of list_competencies_in_course() result value. + * Returns description of list_course_competencies() result value. * * @return \external_description */ - public static function list_competencies_in_course_returns() { - return new external_multiple_structure(competency_exporter::get_read_structure()); + public static function list_course_competencies_returns() { + + return new external_multiple_structure( + new external_single_structure(array( + 'competency' => competency_exporter::get_read_structure(), + 'coursecompetency' => course_competency_exporter::get_read_structure() + )) + ); + } /** @@ -3640,4 +3653,59 @@ class external extends external_api { 'cohorts' => new external_multiple_structure(cohort_summary_exporter::get_read_structure()) )); } + + /** + * Returns description of update_ruleoutcome_course_competency() parameters. + * + * @return \external_function_parameters + */ + public static function set_ruleoutcome_course_competency_parameters() { + $coursecompetencyid = new external_value( + PARAM_INT, + 'Data base record id for the course competency', + VALUE_REQUIRED + ); + + $ruleoutcome = new external_value( + PARAM_INT, + 'Ruleoutcome value', + VALUE_REQUIRED + ); + + $params = array( + 'coursecompetencyid' => $coursecompetencyid, + 'ruleoutcome' => $ruleoutcome, + ); + return new external_function_parameters($params); + } + + /** + * Change the ruleoutcome of a course competency. + * + * @param int $coursecompetencyid The course competency id + * @param int $ruleoutcome The ruleoutcome value + * @return bool + */ + public static function set_ruleoutcome_course_competency($coursecompetencyid, $ruleoutcome) { + $params = self::validate_parameters(self::set_ruleoutcome_course_competency_parameters(), + array( + 'coursecompetencyid' => $coursecompetencyid, + 'ruleoutcome' => $ruleoutcome, + )); + + $coursecompetency = new course_competency($params['coursecompetencyid']); + self::validate_context(context_course::instance($coursecompetency->get_courseid())); + + return api::set_ruleoutcome_course_competency($params['coursecompetencyid'], $params['ruleoutcome']); + } + + /** + * Returns description of update_ruleoutcome_course_competency() result value. + * + * @return \external_value + */ + public static function set_ruleoutcome_course_competency_returns() { + return new external_value(PARAM_BOOL, 'True if the update was successful'); + } + } diff --git a/admin/tool/lp/classes/output/course_competencies_page.php b/admin/tool/lp/classes/output/course_competencies_page.php index 5afcc77baad..6d3f1e3a0d9 100644 --- a/admin/tool/lp/classes/output/course_competencies_page.php +++ b/admin/tool/lp/classes/output/course_competencies_page.php @@ -31,7 +31,10 @@ use moodle_url; use context_system; use context_course; use tool_lp\api; +use tool_lp\course_competency; +use tool_lp\competency; use tool_lp\external\competency_exporter; +use tool_lp\external\course_competency_exporter; /** * Class containing data for course competencies page @@ -47,8 +50,8 @@ class course_competencies_page implements renderable, templatable { /** @var context $context The context for this page. */ protected $context = null; - /** @var \tool_lp\competency[] $competencies List of competencies. */ - protected $competencies = array(); + /** @var \tool_lp\course_competency[] $competencies List of competencies. */ + protected $coursecompetencylist = array(); /** @var bool $canmanagecompetencyframeworks Can the current user manage competency frameworks. */ protected $canmanagecompetencyframeworks = false; @@ -66,7 +69,7 @@ class course_competencies_page implements renderable, templatable { public function __construct($courseid) { $this->context = context_course::instance($courseid); $this->courseid = $courseid; - $this->competencies = api::list_competencies_in_course($courseid); + $this->coursecompetencylist = api::list_course_competencies($courseid); $this->canmanagecoursecompetencies = has_capability('tool/lp:coursecompetencymanage', $this->context); // Check the lowest level in which the user can manage the competencies. @@ -95,16 +98,27 @@ class course_competencies_page implements renderable, templatable { $data->courseid = $this->courseid; $data->pagecontextid = $this->context->id; $data->competencies = array(); - $contextcache = array(); - foreach ($this->competencies as $competency) { + $ruleoutcomelist = course_competency::get_ruleoutcome_list(); + $ruleoutcomeoptions = array(); + foreach ($ruleoutcomelist as $value => $text) { + $ruleoutcomeoptions[] = array('value' => $value, 'text' => (string) $text); + } + foreach ($this->coursecompetencylist as $coursecompetencyelement) { + $coursecompetency = $coursecompetencyelement['coursecompetency']; + $competency = $coursecompetencyelement['competency']; if (!isset($contextcache[$competency->get_competencyframeworkid()])) { $contextcache[$competency->get_competencyframeworkid()] = $competency->get_context(); } $context = $contextcache[$competency->get_competencyframeworkid()]; - $exporter = new competency_exporter($competency, array('context' => $context)); $record = $exporter->export($output); + $exporter = new course_competency_exporter($coursecompetency, array('context' => $context)); + $recordcoursecompetency = $exporter->export($output); + $record->coursecompetencyid = $recordcoursecompetency->id; + $record->coursecompetencyoutcomelabel = get_string('coursecompetencyoutcomelabel', 'tool_lp'); + $record->ruleoutcomeoptions = $ruleoutcomeoptions; + $record->ruleoutcomeoptions[$recordcoursecompetency->ruleoutcome]["selected"] = true; array_push($data->competencies, $record); } $data->canmanagecompetencyframeworks = $this->canmanagecompetencyframeworks; @@ -116,4 +130,5 @@ class course_competencies_page implements renderable, templatable { return $data; } + } diff --git a/admin/tool/lp/db/install.xml b/admin/tool/lp/db/install.xml index b29177e1bd7..3aa748dbdce 100644 --- a/admin/tool/lp/db/install.xml +++ b/admin/tool/lp/db/install.xml @@ -61,6 +61,7 @@ + @@ -71,6 +72,9 @@ + + + diff --git a/admin/tool/lp/db/services.php b/admin/tool/lp/db/services.php index a5553429250..0c85767d99d 100644 --- a/admin/tool/lp/db/services.php +++ b/admin/tool/lp/db/services.php @@ -217,9 +217,9 @@ $functions = array( 'capabilities' => 'tool/lp:competencymanage', 'ajax' => true, ), - 'tool_lp_list_competencies_in_course' => array( + 'tool_lp_list_course_competencies' => array( 'classname' => 'tool_lp\external', - 'methodname' => 'list_competencies_in_course', + 'methodname' => 'list_course_competencies', 'classpath' => '', 'description' => 'List the competencies in a course', 'type' => 'read', @@ -280,6 +280,15 @@ $functions = array( 'capabilities' => 'tool/lp:coursecompetencymanage', 'ajax' => true, ), + 'tool_lp_set_ruleoutcome_course_competency' => array( + 'classname' => 'tool_lp\external', + 'methodname' => 'set_ruleoutcome_course_competency', + 'classpath' => '', + 'description' => 'Modify the ruleoutcome value for course competency', + 'type' => 'write', + 'capabilities' => 'tool/lp:coursecompetencymanage', + 'ajax' => true, + ), 'tool_lp_remove_competency_from_template' => array( 'classname' => 'tool_lp\external', 'methodname' => 'remove_competency_from_template', diff --git a/admin/tool/lp/db/upgrade.php b/admin/tool/lp/db/upgrade.php index f6988e61538..7dfe5f88f10 100644 --- a/admin/tool/lp/db/upgrade.php +++ b/admin/tool/lp/db/upgrade.php @@ -510,5 +510,26 @@ function xmldb_tool_lp_upgrade($oldversion) { upgrade_plugin_savepoint(true, 2015111020, 'tool', 'lp'); } + if ($oldversion < 2015111021) { + + // Define field ruleoutcome to be added to tool_lp_course_competency. + $table = new xmldb_table('tool_lp_course_competency'); + $field = new xmldb_field('ruleoutcome', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, 0, 'competencyid'); + $index = new xmldb_index('courseidruleoutcome', XMLDB_INDEX_NOTUNIQUE, array('courseid', 'ruleoutcome')); + + // Conditionally launch add field ruleoutcome. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Conditionally launch add index ruleoutcome. + if (!$dbman->index_exists($table, $index)) { + $dbman->add_index($table, $index); + } + + // Lp savepoint reached. + upgrade_plugin_savepoint(true, 2015111021, 'tool', 'lp'); + } + return true; } diff --git a/admin/tool/lp/lang/en/tool_lp.php b/admin/tool/lp/lang/en/tool_lp.php index 7b70dec0a15..67893992578 100644 --- a/admin/tool/lp/lang/en/tool_lp.php +++ b/admin/tool/lp/lang/en/tool_lp.php @@ -56,6 +56,11 @@ $string['completeplanconfirm'] = 'Set the plan \'{$a}\' to completed? The curren $string['completeplanstask'] = 'Complete plans which are due'; $string['configurescale'] = 'Configure scales'; $string['coursecompetencies'] = 'Course competencies'; +$string['coursecompetencyoutcome_complete'] = 'Marked as complete'; +$string['coursecompetencyoutcome_evidence'] = 'Attached to evidence'; +$string['coursecompetencyoutcomelabel'] = 'When the course is completed the competency is'; +$string['coursecompetencyoutcome_none'] = 'None'; +$string['coursecompetencyoutcome_recommend'] = 'Recommended'; $string['coursesusingthiscompetency'] = 'Courses linked to this competency'; $string['coveragesummary'] = '{$a->competenciescoveredcount} of {$a->competenciescount} competencies are covered ( {$a->coveragepercentage} % )'; $string['createplans'] = 'Create plans'; diff --git a/admin/tool/lp/templates/course_competencies_page.mustache b/admin/tool/lp/templates/course_competencies_page.mustache index ee368da378e..2b96a0a1d85 100644 --- a/admin/tool/lp/templates/course_competencies_page.mustache +++ b/admin/tool/lp/templates/course_competencies_page.mustache @@ -27,6 +27,14 @@ {{> tool_lp/competency_summary }} + {{#coursecompetencyoutcomelabel}} + {{coursecompetencyoutcomelabel}} + + {{/coursecompetencyoutcomelabel}} {{/competencies}} diff --git a/admin/tool/lp/tests/api_test.php b/admin/tool/lp/tests/api_test.php index 93d486362d6..2b9e66ce0f8 100644 --- a/admin/tool/lp/tests/api_test.php +++ b/admin/tool/lp/tests/api_test.php @@ -1084,4 +1084,31 @@ class tool_lp_api_testcase extends advanced_testcase { $this->assertEquals(1, \tool_lp\user_competency::count_records_select('id = :id', array('id' => $evidence->get_usercompetencyid()))); } + + /** + * Test update ruleoutcome for course_competency. + */ + public function test_set_rueloutcome_on_course_competency() { + $this->resetAfterTest(true); + $dg = $this->getDataGenerator(); + $lpg = $dg->get_plugin_generator('tool_lp'); + $u1 = $dg->create_user(); + $u2 = $dg->create_user(); + + $this->setAdminUser(); + $f = $lpg->create_framework(); + $c = $lpg->create_competency(array('competencyframeworkid' => $f->get_id())); + $cc = api::add_competency_to_course(1, $c->get_id()); + + // Check record was created with default rule value Evidence. + $this->assertEquals(1, \tool_lp\course_competency::count_records()); + $recordscc = api::list_course_competencies(1); + $this->assertEquals(1, $recordscc[0]['coursecompetency']->get_ruleoutcome()); + + // Check ruleoutcome value is updated to None. + $this->assertTrue(api::set_ruleoutcome_course_competency($recordscc[0]['coursecompetency']->get_id(), 0)); + $recordscc = api::list_course_competencies(1); + $this->assertEquals(0, $recordscc[0]['coursecompetency']->get_ruleoutcome()); + } + } diff --git a/admin/tool/lp/version.php b/admin/tool/lp/version.php index 6cad0e78dc8..719c4f09169 100644 --- a/admin/tool/lp/version.php +++ b/admin/tool/lp/version.php @@ -25,7 +25,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2015111020; // The current plugin version (Date: YYYYMMDDXX). +$plugin->version = 2015111021; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2014110400; // Requires this Moodle version. $plugin->component = 'tool_lp'; // Full name of the plugin (used for diagnostics). -