MDL-51029 tool_lp: Implement competency rules
This commit is contained in:
@@ -0,0 +1 @@
|
||||
define(["jquery","core/str"],function(a,b){var c=0,d=1,e=2,f=3;return{NONE:c,EVIDENCE:d,COMPLETE:e,RECOMMEND:f,getAll:function(){var a=this;return b.get_strings([{key:"competencyoutcome_none",component:"tool_lp"},{key:"competencyoutcome_evidence",component:"tool_lp"},{key:"competencyoutcome_recommend",component:"tool_lp"},{key:"competencyoutcome_complete",component:"tool_lp"}]).then(function(b){var c={};return c[a.NONE]={code:a.NONE,name:b[0]},c[a.EVIDENCE]={code:a.EVIDENCE,name:b[1]},c[a.RECOMMEND]={code:a.RECOMMEND,name:b[2]},c[a.COMPLETE]={code:a.COMPLETE,name:b[3]},c})},getString:function(b){var c=this,d=c.getAll();return d.then(function(c){return"undefined"==typeof c[b]?a.Deferred().reject().promise():c[b].name})}}});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery"],function(a){var b=function(b){this._eventNode=a("<div>"),this._ready=a.Deferred(),this._tree=b};return b.prototype._competency=null,b.prototype._eventNode=null,b.prototype._ready=null,b.prototype._tree=null,b.prototype.canConfig=function(){return this._tree.hasChildren(this._competency.id)},b.prototype.getConfig=function(){return null},b.prototype.getType=function(){throw new Error("Not implemented")},b.prototype.init=function(){return this._load()},b.prototype.injectTemplate=function(){return a.Deferred().reject().promise()},b.prototype.isValid=function(){return!1},b.prototype._load=function(){return a.when()},b.prototype.on=function(a,b){this._eventNode.on(a,b)},b.prototype.setTargetCompetency=function(a){this._competency=a},b.prototype._trigger=function(a,b){this._eventNode.trigger(a,[b])},b.prototype._triggerChange=function(){this._trigger("change",this)},b});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/str","tool_lp/competency_rule"],function(a,b,c){var d=function(){c.apply(this,arguments)};return d.prototype=Object.create(c.prototype),d.prototype.getType=function(){return"tool_lp\\competency_rule_all"},d.prototype.isValid=function(){return!0},d});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/str","core/templates","tool_lp/competency_rule"],function(a,b,c,d){var e=function(){d.apply(this,arguments)};return e.prototype=Object.create(d.prototype),e.prototype._container=null,e.prototype._templateLoaded=!1,e.prototype.getConfig=function(){return JSON.stringify({base:{points:this._getRequiredPoints()},competencies:this._getCompetenciesConfig()})},e.prototype._getCompetenciesConfig=function(){var b=[];return this._container.find("[data-competency]").each(function(){var c=a(this),d=c.data("competency"),e=parseInt(c.find('[name="points"]').val(),10),f=c.find('[name="required"]').prop("checked");b.push({id:d,points:e,required:f?1:0})}),b},e.prototype._getRequiredPoints=function(){return parseInt(this._container.find('[name="requiredpoints"]').val()||1,10)},e.prototype.getType=function(){return"tool_lp\\competency_rule_points"},e.prototype.injectTemplate=function(b){var d,e=this,f=this._tree.getChildren(this._competency.id),g={base:{points:2},competencies:[]};if(this._templateLoaded=!1,e._competency.ruletype==e.getType())try{g=JSON.parse(e._competency.ruleconfig)}catch(h){}return d={requiredpoints:g&&g.base?g.base.points:2,competency:e._competency,children:[]},a.each(f,function(b,c){var e={id:c.id,shortname:c.shortname,required:!1,points:0};g&&a.each(g.competencies,function(a,b){b.id==e.id&&(e.required=b.required?!0:!1,e.points=b.points)}),d.children.push(e)}),c.render("tool_lp/competency_rule_points",d).then(function(a){e._container=b,b.html(a),b.find("input").change(function(){e._triggerChange()}),e._templateLoaded=!0,e._triggerChange()})},e.prototype.isValid=function(){if(!this._templateLoaded)return!1;var b=this._getRequiredPoints(),c=0,d=!0;return a.each(this._getCompetenciesConfig(),function(a,b){b.points<0&&(d=!1),c+=b.points}),d=d&&c>=b},e});
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define(["jquery","core/notification","core/templates","tool_lp/dialogue","tool_lp/competency_outcomes","core/str"],function(a,b,c,d,e,f){var g=function(b,c){this._eventNode=a("<div></div>"),this._tree=b,this._rulesModules=c,this._setUp()};return g.prototype._competency=null,g.prototype._eventNode=null,g.prototype._outcomesOption=null,g.prototype._popup=null,g.prototype._ready=null,g.prototype._rules=null,g.prototype._rulesModules=null,g.prototype._tree=null,g.prototype._afterChange=function(){this._isValid()?this._find('[data-action="save"]').prop("disabled",!1):this._find('[data-action="save"]').prop("disabled",!0)},g.prototype._afterRuleConfigChange=function(a,b){b==this._getRule()&&this._afterChange()},g.prototype._afterRender=function(){var a=this;a._find('[name="outcome"]').on("change",function(){a._switchedOutcome()}).trigger("change"),a._find('[name="rule"]').on("change",function(){a._switchedRule()}).trigger("change"),a._find('[data-action="save"]').on("click",function(){a._trigger("save",a._getConfig()),a.close()}),a._find('[data-action="cancel"]').on("click",function(){a.close()})},g.prototype.canBeConfigured=function(){var b=!1;return a.each(this._rules,function(a,c){return c.canConfig()?(b=!0,!1):void 0}),b},g.prototype.close=function(){this._popup.close(),this._popup=null},g.prototype.display=function(){var a=this;if(a._competency)return a._render().then(function(b){return f.get_string("competencyrule","tool_lp").then(function(c){a._popup=new d(c,b,a._afterRender.bind(a))})}).fail(b.exception)},g.prototype._find=function(b){return a(this._popup.getContent()).find(b)},g.prototype._getApplicableOutcomesOptions=function(){var b=this,c=[];return a.each(b._outcomesOption,function(a,d){c.push({code:d.code,name:d.name,selected:d.code==b._competency.ruleoutcome?!0:!1})}),c},g.prototype._getApplicableRulesOptions=function(){var b=this,c=[];return a.each(b._rules,function(a,d){d.canConfig()&&c.push({name:b._getRuleName(d.getType()),type:d.getType(),selected:d.getType()==b._competency.ruletype?!0:!1})}),c},g.prototype._getConfig=function(){var a=this._getRule();return{ruletype:a?a.getType():null,ruleconfig:a?a.getConfig():null,ruleoutcome:this._getOutcome()}},g.prototype._getOutcome=function(){return this._find('[name="outcome"]').val()},g.prototype._getRule=function(){var b,c=this._find('[name="rule"]').val();return a.each(this._rules,function(a,d){return d.getType()==c?(b=d,!1):void 0}),b},g.prototype._getRuleName=function(b){var c,d=this;return a.each(d._rulesModules,function(a,d){return d.type==b?(c=d.name,!1):void 0}),c},g.prototype._initOutcomes=function(){var a=this;return e.getAll().then(function(b){a._outcomesOption=b})},g.prototype._initRules=function(){var b=this,c=[];return a.each(b._rules,function(d,e){var f=e.init().then(function(){e.setTargetCompetency(b._competency),e.on("change",b._afterRuleConfigChange.bind(b))},function(){return b._rules.splice(d,1),a.when()});c.push(f)}),a.when.apply(a.when,c)},g.prototype._isValid=function(){var a=this._getOutcome(),b=this._getRule();return a==e.NONE?!0:b?b.isValid():!1},g.prototype.on=function(a,b){this._eventNode.on(a,b)},g.prototype._preRender=function(){return this.ready()},g.prototype.ready=function(){return this._ready.promise()},g.prototype._render=function(){var a=this;return this._preRender().then(function(){var b;a.canBeConfigured()?(b={},b.outcomes=a._getApplicableOutcomesOptions(),b.rules=a._getApplicableRulesOptions()):b=!1;var d={competencyshortname:a._competency.shortname,config:b};return c.render("tool_lp/competency_rule_config",d)})},g.prototype.setTargetCompetencyId=function(b){var c=this;c._competency=c._tree.getCompetency(b),a.each(c._rules,function(a,b){b.setTargetCompetency(c._competency)})},g.prototype._setUp=function(){var b=this,c=[],d=[];b._ready=a.Deferred(),b._rules=[],a.each(b._rulesModules,function(a,b){d.push(b.amd)}),require(d,function(){a.each(arguments,function(a,c){var d=new c(b._tree);b._rules.push(d)}),c.push(b._initRules()),c.push(b._initOutcomes()),a.when.apply(a.when,c).always(function(){b._ready.resolve()})})},g.prototype._switchedOutcome=function(){var a=this,b=a._getOutcome();return b==e.NONE?(a._find('[data-region="rule-type"]').hide().find('[name="rule"]').val(-1),a._find('[data-region="rule-config"]').empty().hide(),void a._afterChange()):(a._find('[data-region="rule-type"]').show(),a._find('[data-region="rule-config"]').show(),void a._afterChange())},g.prototype._switchedRule=function(){var a=this,b=a._find('[data-region="rule-config"]'),c=a._getRule();return c?void c.injectTemplate(b).then(function(){b.show()},function(){b.empty().hide()}).always(function(){a._afterChange()}):(b.empty().hide(),void a._afterChange())},g.prototype._trigger=function(a,b){this._eventNode.trigger(a,[b])},g});
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["core/ajax","core/notification","core/templates","tool_lp/tree","jquery"],function(a,b,c,d,e){var f=[],g=0,h="",i="",j="",k=function(a,b){var c=0,d=!1;for(a.haschildren=!1,a.children=[],c=0;c<b.length;c++)d=b[c],d.parentid==a.id&&(a.haschildren=!0,a.children.push(d),k(d,b))},l=function(b){var l=e.Deferred();return c.render("tool_lp/loading",{}).done(function(m,n){c.replaceNodeContents(e(i),m,n);var o=a.call([{methodname:"tool_lp_search_competencies",args:{searchtext:b,competencyframeworkid:g,includerelated:!0}}]);o[0].done(function(a){f=[];var b=0;for(b=0;b<a.length;b++)f[a[b].id]=a[b];var g=[],m=!1;for(b=0;b<a.length;b++)m=a[b],0===parseInt(m.parentid,10)&&(g.push(m),k(m,a));var n={shortname:h,competencies:g};c.render("tool_lp/competencies_tree_root",n).done(function(a,b){c.replaceNodeContents(e(i),e(a).html(),b);var g=new d(i);if(j){var h=e(i).find("[data-id="+j+"]");h.length&&g.updateFocus(h)}l.resolve(f)}).fail(l.reject)}).fail(l.reject)}),l.promise()},m=function(a,b){j=b.attr("data-id")};return{init:function(a,c,d,e){g=a,h=c,i=e,l(d).fail(b.exception),this.on("selectionchanged",m)},on:function(a,b){e(i).on(a,b)},getCompetencyFrameworkId:function(){return g},getCompetency:function(a){return f[a]},getCompetencyLevel:function(a){var b=this.getCompetency(a),c=b.path.replace(/^\/|\/$/g,"").split("/").length;return c},reloadCompetencies:function(){return l("").fail(b.exception)},listCompetencies:function(){return f}}});
|
||||
define(["core/ajax","core/notification","core/templates","tool_lp/tree","jquery"],function(a,b,c,d,e){var f={},g=0,h="",i="",j="",k=function(a,b){var c=0,d=!1;for(a.haschildren=!1,a.children=[],c=0;c<b.length;c++)d=b[c],d.parentid==a.id&&(a.haschildren=!0,a.children.push(d),k(d,b))},l=function(b){var l=e.Deferred();return c.render("tool_lp/loading",{}).done(function(m,n){c.replaceNodeContents(e(i),m,n);var o=a.call([{methodname:"tool_lp_search_competencies",args:{searchtext:b,competencyframeworkid:g,includerelated:!0}}]);o[0].done(function(a){f=[];var b=0;for(b=0;b<a.length;b++)f[a[b].id]=a[b];var g=[],m=!1;for(b=0;b<a.length;b++)m=a[b],0===parseInt(m.parentid,10)&&(g.push(m),k(m,a));var n={shortname:h,competencies:g};c.render("tool_lp/competencies_tree_root",n).done(function(a,b){c.replaceNodeContents(e(i),e(a).html(),b);var g=new d(i);if(j){var h=e(i).find("[data-id="+j+"]");h.length&&g.updateFocus(h)}l.resolve(f)}).fail(l.reject)}).fail(l.reject)}),l.promise()},m=function(a,b){j=b.attr("data-id")};return{init:function(a,c,d,e){g=a,h=c,i=e,l(d).fail(b.exception),this.on("selectionchanged",m)},on:function(a,b){e(i).on(a,b)},getChildren:function(a){var b=[];return e.each(f,function(c,d){d.parentid==a&&b.push(d)}),b},getCompetencyFrameworkId:function(){return g},getCompetency:function(a){return f[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},reloadCompetencies:function(){return l("").fail(b.exception)},listCompetencies:function(){return f}}});
|
||||
@@ -0,0 +1,83 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Competency rule config.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery',
|
||||
'core/str'],
|
||||
function($, Str) {
|
||||
|
||||
var OUTCOME_NONE = 0,
|
||||
OUTCOME_EVIDENCE = 1,
|
||||
OUTCOME_COMPLETE = 2,
|
||||
OUTCOME_RECOMMEND = 3;
|
||||
|
||||
return /** @alias module:tool_lp/competency_outcomes */ {
|
||||
|
||||
NONE: OUTCOME_NONE,
|
||||
EVIDENCE: OUTCOME_EVIDENCE,
|
||||
COMPLETE: OUTCOME_COMPLETE,
|
||||
RECOMMEND: OUTCOME_RECOMMEND,
|
||||
|
||||
/**
|
||||
* Get all the outcomes.
|
||||
*
|
||||
* @return {Object} Indexed by outcome code, contains code and name.
|
||||
* @method getAll
|
||||
*/
|
||||
getAll: function() {
|
||||
var self = this;
|
||||
return Str.get_strings([
|
||||
{ key: 'competencyoutcome_none', component: 'tool_lp' },
|
||||
{ key: 'competencyoutcome_evidence', component: 'tool_lp' },
|
||||
{ key: 'competencyoutcome_recommend', component: 'tool_lp' },
|
||||
{ key: 'competencyoutcome_complete', component: 'tool_lp' },
|
||||
]).then(function(strings) {
|
||||
var outcomes = {};
|
||||
outcomes[self.NONE] = { code: self.NONE, name: strings[0] };
|
||||
outcomes[self.EVIDENCE] = { code: self.EVIDENCE, name: strings[1] };
|
||||
outcomes[self.RECOMMEND] = { code: self.RECOMMEND, name: strings[2] };
|
||||
outcomes[self.COMPLETE] = { code: self.COMPLETE, name: strings[3] };
|
||||
return outcomes;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the string for an outcome.
|
||||
*
|
||||
* @param {Number} id The outcome code.
|
||||
* @return {Promise Resolved with the string.
|
||||
* @method getString
|
||||
*/
|
||||
getString: function(id) {
|
||||
var self = this,
|
||||
all = self.getAll();
|
||||
|
||||
return all.then(function(outcomes) {
|
||||
if (typeof outcomes[id] === 'undefined') {
|
||||
return $.Deferred().reject().promise();
|
||||
}
|
||||
return outcomes[id].name;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,175 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Competency rule base module.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery'], function($) {
|
||||
|
||||
/**
|
||||
* Competency rule abstract class.
|
||||
*
|
||||
* Any competency rule should extend this object. The event 'change' should be
|
||||
* triggered on the instance when the configuration has changed. This will allow
|
||||
* the components using the rule to gather the config, or check its validity.
|
||||
*
|
||||
* this._triggerChange();
|
||||
*
|
||||
* @param {Tree} tree The competency tree.
|
||||
*/
|
||||
var Rule = function(tree) {
|
||||
this._eventNode = $('<div>');
|
||||
this._ready = $.Deferred();
|
||||
this._tree = tree;
|
||||
};
|
||||
|
||||
/** @type {Object} The current competency. */
|
||||
Rule.prototype._competency = null;
|
||||
/** @type {Node} The node we attach the events to. */
|
||||
Rule.prototype._eventNode = null;
|
||||
/** @type {Promise} Resolved when the object is ready. */
|
||||
Rule.prototype._ready = null;
|
||||
/** @type {Tree} The competency tree. */
|
||||
Rule.prototype._tree = null;
|
||||
|
||||
/**
|
||||
* Whether or not the current competency can be configured using this rule.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @method canConfig
|
||||
*/
|
||||
Rule.prototype.canConfig = function() {
|
||||
return this._tree.hasChildren(this._competency.id);
|
||||
};
|
||||
|
||||
/**
|
||||
* The config established by this rule.
|
||||
*
|
||||
* To override in subclasses when relevant.
|
||||
*
|
||||
* @return {String|null}
|
||||
* @method getConfig
|
||||
*/
|
||||
Rule.prototype.getConfig = function() {
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the type of the module.
|
||||
*
|
||||
* @return {String}
|
||||
* @method getType
|
||||
*/
|
||||
Rule.prototype.getType = function() {
|
||||
throw new Error('Not implemented');
|
||||
};
|
||||
|
||||
/**
|
||||
* The init process.
|
||||
*
|
||||
* Do not override this, instead override _load.
|
||||
*
|
||||
* @return {Promise} Revoled when the plugin is initialised.
|
||||
* @method init
|
||||
*/
|
||||
Rule.prototype.init = function() {
|
||||
return this._load();
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to inject the template.
|
||||
*
|
||||
* @param {Node} container Node to inject in.
|
||||
* @return {Promise} Resolved when done.
|
||||
* @method injectTemplate
|
||||
*/
|
||||
Rule.prototype.injectTemplate = function() {
|
||||
return $.Deferred().reject().promise();
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether or not the current config is valid.
|
||||
*
|
||||
* Plugins should override this.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @method _isValid
|
||||
*/
|
||||
Rule.prototype.isValid = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the class.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @method _load
|
||||
* @protected
|
||||
*/
|
||||
Rule.prototype._load = function() {
|
||||
return $.when();
|
||||
};
|
||||
|
||||
/**
|
||||
* Register an event listener.
|
||||
*
|
||||
* @param {String} type The event type.
|
||||
* @param {Function} handler The event listener.
|
||||
* @method on
|
||||
*/
|
||||
Rule.prototype.on = function(type, handler) {
|
||||
this._eventNode.on(type, handler);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the current competency.
|
||||
*
|
||||
* @param {Competency} competency
|
||||
* @method setTargetCompetency
|
||||
*/
|
||||
Rule.prototype.setTargetCompetency = function(competency) {
|
||||
this._competency = competency;
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger an event.
|
||||
*
|
||||
* @param {String} type The type of event.
|
||||
* @param {Object} The data to pass to the listeners.
|
||||
* @method _trigger
|
||||
* @protected
|
||||
*/
|
||||
Rule.prototype._trigger = function(type, data) {
|
||||
this._eventNode.trigger(type, [data]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger the change event.
|
||||
*
|
||||
* @method _triggerChange
|
||||
* @protected
|
||||
*/
|
||||
Rule.prototype._triggerChange = function() {
|
||||
this._trigger('change', this);
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/competency_rule */ Rule;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Competency rule all module.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery',
|
||||
'core/str',
|
||||
'tool_lp/competency_rule',
|
||||
],
|
||||
function($, Str, RuleBase) {
|
||||
|
||||
/**
|
||||
* Competency rule all class.
|
||||
*/
|
||||
var Rule = function() {
|
||||
RuleBase.apply(this, arguments);
|
||||
};
|
||||
Rule.prototype = Object.create(RuleBase.prototype);
|
||||
|
||||
/**
|
||||
* Return the type of the module.
|
||||
*
|
||||
* @return {String}
|
||||
* @method getType
|
||||
*/
|
||||
Rule.prototype.getType = function() {
|
||||
return 'tool_lp\\competency_rule_all';
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether or not the current config is valid.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @method isValid
|
||||
*/
|
||||
Rule.prototype.isValid = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/competency_rule_all */ Rule;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,199 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Competency rule points module.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery',
|
||||
'core/str',
|
||||
'core/templates',
|
||||
'tool_lp/competency_rule',
|
||||
],
|
||||
function($, Str, Templates, RuleBase) {
|
||||
|
||||
/**
|
||||
* Competency rule points class.
|
||||
*/
|
||||
var Rule = function() {
|
||||
RuleBase.apply(this, arguments);
|
||||
};
|
||||
Rule.prototype = Object.create(RuleBase.prototype);
|
||||
|
||||
/** @type {Node} Reference to the container in which the template was included. */
|
||||
Rule.prototype._container = null;
|
||||
/** @type {Boolean} Whether or not the template was included. */
|
||||
Rule.prototype._templateLoaded = false;
|
||||
|
||||
/**
|
||||
* The config established by this rule.
|
||||
*
|
||||
* @return {String}
|
||||
* @method getConfig
|
||||
*/
|
||||
Rule.prototype.getConfig = function() {
|
||||
return JSON.stringify({
|
||||
base: {
|
||||
points: this._getRequiredPoints(),
|
||||
},
|
||||
competencies: this._getCompetenciesConfig()
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gathers the input provided by the user for competencies.
|
||||
*
|
||||
* @return {Array} Containing id, points and required.
|
||||
* @method _getCompetenciesConfig
|
||||
* @protected
|
||||
*/
|
||||
Rule.prototype._getCompetenciesConfig = function() {
|
||||
var competencies = [];
|
||||
|
||||
this._container.find('[data-competency]').each(function() {
|
||||
var node = $(this),
|
||||
id = node.data('competency'),
|
||||
points = parseInt(node.find('[name="points"]').val(), 10),
|
||||
required = node.find('[name="required"]').prop('checked');
|
||||
|
||||
competencies.push({
|
||||
id: id,
|
||||
points: points,
|
||||
required: required ? 1 : 0
|
||||
});
|
||||
});
|
||||
|
||||
return competencies;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches the required points set by the user.
|
||||
*
|
||||
* @return {Number}
|
||||
* @method _getRequiredPoints
|
||||
* @protected
|
||||
*/
|
||||
Rule.prototype._getRequiredPoints = function() {
|
||||
return parseInt(this._container.find('[name="requiredpoints"]').val() || 1, 10);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the type of the module.
|
||||
*
|
||||
* @return {String}
|
||||
* @method getType
|
||||
*/
|
||||
Rule.prototype.getType = function() {
|
||||
return 'tool_lp\\competency_rule_points';
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to inject the template.
|
||||
*
|
||||
* @param {Node} container Node to inject in.
|
||||
* @return {Promise} Resolved when done.
|
||||
* @method injectTemplate
|
||||
*/
|
||||
Rule.prototype.injectTemplate = function(container) {
|
||||
var self = this,
|
||||
children = this._tree.getChildren(this._competency.id),
|
||||
context,
|
||||
config = {
|
||||
base: { points: 2 },
|
||||
competencies: []
|
||||
};
|
||||
|
||||
this._templateLoaded = false;
|
||||
|
||||
// Only pre-load the configuration when the competency is using this rule.
|
||||
if (self._competency.ruletype == self.getType()) {
|
||||
try {
|
||||
config = JSON.parse(self._competency.ruleconfig);
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
context = {
|
||||
requiredpoints: (config && config.base) ? config.base.points : 2,
|
||||
competency: self._competency,
|
||||
children: []
|
||||
};
|
||||
|
||||
$.each(children, function(index, child) {
|
||||
var competency = {
|
||||
id: child.id,
|
||||
shortname: child.shortname,
|
||||
required: false,
|
||||
points: 0
|
||||
};
|
||||
|
||||
if (config) {
|
||||
$.each(config.competencies, function(index, comp) {
|
||||
if (comp.id == competency.id) {
|
||||
competency.required = comp.required ? true : false;
|
||||
competency.points = comp.points;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
context.children.push(competency);
|
||||
});
|
||||
|
||||
return Templates.render('tool_lp/competency_rule_points', context).then(function(html) {
|
||||
self._container = container;
|
||||
container.html(html);
|
||||
container.find('input').change(function() {
|
||||
self._triggerChange();
|
||||
});
|
||||
|
||||
// We're done, let's trigger a change.
|
||||
self._templateLoaded = true;
|
||||
self._triggerChange();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether or not the current config is valid.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @method isValid
|
||||
*/
|
||||
Rule.prototype.isValid = function() {
|
||||
if (!this._templateLoaded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var required = this._getRequiredPoints(),
|
||||
max = 0,
|
||||
valid = true;
|
||||
|
||||
$.each(this._getCompetenciesConfig(), function(index, competency) {
|
||||
if (competency.points < 0) {
|
||||
valid = false;
|
||||
}
|
||||
max += competency.points;
|
||||
});
|
||||
|
||||
valid = valid && max >= required;
|
||||
return valid;
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/competency_rule_all */ Rule;
|
||||
|
||||
});
|
||||
@@ -31,8 +31,10 @@ define(['jquery',
|
||||
'tool_lp/tree',
|
||||
'tool_lp/dialogue',
|
||||
'tool_lp/menubar',
|
||||
'tool_lp/competencypicker'],
|
||||
function($, url, templates, notification, str, ajax, dragdrop, Ariatree, Dialogue, menubar, Picker) {
|
||||
'tool_lp/competencypicker',
|
||||
'tool_lp/competency_outcomes',
|
||||
'tool_lp/competencyruleconfig'],
|
||||
function($, url, templates, notification, str, ajax, dragdrop, Ariatree, Dialogue, menubar, Picker, Outcomes, RuleConfig) {
|
||||
|
||||
// Private variables and functions.
|
||||
/** @var {Object} treeModel - This is an object representing the nodes in the tree. */
|
||||
@@ -45,10 +47,14 @@ define(['jquery',
|
||||
var pageContextId;
|
||||
/** @type {Object} Picker instance. */
|
||||
var pickerInstance;
|
||||
/** @type {Object} Rule config instance. */
|
||||
var ruleConfigInstance;
|
||||
/** @type {Object} The competency we're picking a relation to. */
|
||||
var relatedTarget;
|
||||
/** @type {Object} Taxonomy constants indexed per level. */
|
||||
var taxonomiesConstants;
|
||||
/** @type {Array} The rules modules. Values are object containing type, namd and amd. */
|
||||
var rulesModules;
|
||||
|
||||
/**
|
||||
* Respond to choosing the "Add" menu item for the selected node in the tree.
|
||||
@@ -347,6 +353,38 @@ define(['jquery',
|
||||
pickerInstance.display();
|
||||
};
|
||||
|
||||
var ruleConfigHandler = function(e) {
|
||||
e.preventDefault();
|
||||
relatedTarget = $('[data-region="competencyactions"]').data('competency');
|
||||
ruleConfigInstance.setTargetCompetencyId(relatedTarget.id);
|
||||
ruleConfigInstance.display();
|
||||
};
|
||||
|
||||
var ruleConfigSaveHandler = function(e, config) {
|
||||
var promise = ajax.call([{
|
||||
methodname: 'tool_lp_update_competency',
|
||||
args: {
|
||||
id: relatedTarget.id,
|
||||
shortname: relatedTarget.shortname,
|
||||
idnumber: relatedTarget.idnumber,
|
||||
description: relatedTarget.description,
|
||||
descriptionformat: relatedTarget.descriptionformat,
|
||||
visible: relatedTarget.visible,
|
||||
ruletype: config.ruletype,
|
||||
ruleoutcome: config.ruleoutcome,
|
||||
ruleconfig: config.ruleconfig,
|
||||
}
|
||||
}]);
|
||||
promise[0].then(function(result) {
|
||||
if (result) {
|
||||
relatedTarget.ruletype = config.ruletype;
|
||||
relatedTarget.ruleoutcome = config.ruleoutcome;
|
||||
relatedTarget.ruleconfig = config.ruleconfig;
|
||||
renderCompetencySummary(relatedTarget);
|
||||
}
|
||||
}, notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a competency.
|
||||
* @method doDelete
|
||||
@@ -375,6 +413,7 @@ define(['jquery',
|
||||
// We don't need to show related actions when showing the competency info.
|
||||
delete competency.showdeleterelatedaction;
|
||||
delete competency.showrelatedcompetencies;
|
||||
delete competency.showrule;
|
||||
|
||||
templates.render('tool_lp/competency_summary', competency)
|
||||
.done(function(html) {
|
||||
@@ -511,6 +550,48 @@ define(['jquery',
|
||||
return constant;
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the competency summary.
|
||||
*
|
||||
* @param {Object} competency The competency.
|
||||
*/
|
||||
var renderCompetencySummary = function(competency) {
|
||||
var promise = $.Deferred().resolve().promise(),
|
||||
context = $.extend({}, competency);
|
||||
|
||||
context.showdeleterelatedaction = true;
|
||||
context.showrelatedcompetencies = true;
|
||||
context.showrule = false;
|
||||
|
||||
if (competency.ruleoutcome != Outcomes.NONE) {
|
||||
// Get the outcome and rule name.
|
||||
promise = Outcomes.getString(competency.ruleoutcome).then(function(str) {
|
||||
var name;
|
||||
$.each(rulesModules, function(index, modInfo) {
|
||||
if (modInfo.type == competency.ruletype) {
|
||||
name = modInfo.name;
|
||||
}
|
||||
});
|
||||
return [str, name];
|
||||
});
|
||||
}
|
||||
|
||||
promise.then(function(strs) {
|
||||
if (typeof strs !== 'undefined') {
|
||||
context.showrule = true;
|
||||
context.rule = {
|
||||
outcome: strs[0],
|
||||
type: strs[1]
|
||||
};
|
||||
}
|
||||
}).then(function() {
|
||||
return templates.render('tool_lp/competency_summary', context).then(function(html) {
|
||||
$('[data-region="competencyinfo"]').html(html);
|
||||
$('[data-action="deleterelation"]').on('click', deleteRelatedHandler);
|
||||
});
|
||||
}).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the string "Add <taxonomy>".
|
||||
*
|
||||
@@ -558,9 +639,6 @@ define(['jquery',
|
||||
} else {
|
||||
var competency = treeModel.getCompetency(id);
|
||||
|
||||
competency.showdeleterelatedaction = true;
|
||||
competency.showrelatedcompetencies = true;
|
||||
|
||||
level = treeModel.getCompetencyLevel(id);
|
||||
if (!hasSubLevel(level)) {
|
||||
sublevel = false;
|
||||
@@ -570,10 +648,7 @@ define(['jquery',
|
||||
|
||||
actionMenu.show();
|
||||
$('[data-region="competencyactions"]').data('competency', competency);
|
||||
templates.render('tool_lp/competency_summary', competency).then(function(html) {
|
||||
$('[data-region="competencyinfo"]').html(html);
|
||||
$('[data-action="deleterelation"]').on('click', deleteRelatedHandler);
|
||||
}, notification.exception);
|
||||
renderCompetencySummary(competency);
|
||||
}
|
||||
|
||||
strSelectedTaxonomy(level).then(function(str) {
|
||||
@@ -602,11 +677,13 @@ define(['jquery',
|
||||
* @param {Object} model The tree model provides some useful functions for loading and searching competencies.
|
||||
* @param {Number} pagectxid The page context ID.
|
||||
* @param {Object} taxonomies Constants indexed by level.
|
||||
* @param {Object} rulesMods The modules of the rules.
|
||||
*/
|
||||
init: function(model, pagectxid, taxonomies) {
|
||||
init: function(model, pagectxid, taxonomies, rulesMods) {
|
||||
treeModel = model;
|
||||
pageContextId = pagectxid;
|
||||
taxonomiesConstants = taxonomies;
|
||||
rulesModules = rulesMods;
|
||||
|
||||
$('[data-region="competencyactions"] [data-action="add"]').on('click', addHandler);
|
||||
|
||||
@@ -617,7 +694,8 @@ define(['jquery',
|
||||
'[data-action="moveup"]': moveUpHandler,
|
||||
'[data-action="movedown"]': moveDownHandler,
|
||||
'[data-action="linkedcourses"]': seeCoursesHandler,
|
||||
'[data-action="relatedcompetencies"]': relateCompetenciesHandler.bind(this)
|
||||
'[data-action="relatedcompetencies"]': relateCompetenciesHandler.bind(this),
|
||||
'[data-action="competencyrules"]': ruleConfigHandler.bind(this)
|
||||
});
|
||||
$('[data-region="competencyactionsmenu"]').hide();
|
||||
$('[data-region="competencyactions"] [data-action="add"]').hide();
|
||||
@@ -631,6 +709,10 @@ define(['jquery',
|
||||
$('[data-region="managecompetencies"] li').on('drop', dropOver);
|
||||
|
||||
model.on('selectionchanged', selectionChanged);
|
||||
|
||||
// Prepare the configuration tool.
|
||||
ruleConfigInstance = new RuleConfig(treeModel, rulesModules);
|
||||
ruleConfigInstance.on('save', ruleConfigSaveHandler.bind(this));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// self file is part of Moodle - http://moodle.org/
|
||||
// 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
|
||||
|
||||
@@ -0,0 +1,548 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Competency rule config.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery',
|
||||
'core/notification',
|
||||
'core/templates',
|
||||
'tool_lp/dialogue',
|
||||
'tool_lp/competency_outcomes',
|
||||
'core/str'],
|
||||
function($, Notification, Templates, Dialogue, Outcomes, Str) {
|
||||
|
||||
/**
|
||||
* Competency rule class.
|
||||
*
|
||||
* When implementing this you should attach a listener to the event 'save'
|
||||
* on the instance. E.g.
|
||||
*
|
||||
* var config = new RuleConfig(tree, modules);
|
||||
* config.on('save', function(e, config) { ... });
|
||||
*
|
||||
* @param {competencytree} tree The competency tree.
|
||||
* @param {Array} rulesModules The modules containing the rules: [{ typeName: { amd: amdModule, name: ruleName }}].
|
||||
*/
|
||||
var RuleConfig = function(tree, rulesModules) {
|
||||
this._eventNode = $('<div></div>');
|
||||
this._tree = tree;
|
||||
this._rulesModules = rulesModules;
|
||||
this._setUp();
|
||||
};
|
||||
|
||||
/** @type {Object} The current competency. */
|
||||
RuleConfig.prototype._competency = null;
|
||||
/** @type {Node} The node we attach the events to. */
|
||||
RuleConfig.prototype._eventNode = null;
|
||||
/** @type {Array} Outcomes options. */
|
||||
RuleConfig.prototype._outcomesOption = null;
|
||||
/** @type {Dialogue} The dialogue. */
|
||||
RuleConfig.prototype._popup = null;
|
||||
/** @type {Promise} Resolved when the module is ready. */
|
||||
RuleConfig.prototype._ready = null;
|
||||
/** @type {Array} The rules. */
|
||||
RuleConfig.prototype._rules = null;
|
||||
/** @type {Array} The rules modules. */
|
||||
RuleConfig.prototype._rulesModules = null;
|
||||
/** @type {competencytree} The competency tree. */
|
||||
RuleConfig.prototype._tree = null;
|
||||
|
||||
/**
|
||||
* After change.
|
||||
*
|
||||
* Triggered when a change occured.
|
||||
*
|
||||
* @return {Void}
|
||||
* @method _afterChange
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._afterChange = function() {
|
||||
if (!this._isValid()) {
|
||||
this._find('[data-action="save"]').prop('disabled', true);
|
||||
} else {
|
||||
this._find('[data-action="save"]').prop('disabled', false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* After change in rule's config.
|
||||
*
|
||||
* Triggered when a change occured in a specific rule config.
|
||||
*
|
||||
* @return {Void}
|
||||
* @method _afterRuleConfigChange
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._afterRuleConfigChange = function(e, rule) {
|
||||
if (rule != this._getRule()) {
|
||||
// This rule is not the current one any more, we can ignore.
|
||||
return;
|
||||
}
|
||||
this._afterChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* After render hook.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @method _afterRender
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._afterRender = function() {
|
||||
var self = this;
|
||||
|
||||
self._find('[name="outcome"]').on('change', function() {
|
||||
self._switchedOutcome();
|
||||
}).trigger('change');
|
||||
|
||||
self._find('[name="rule"]').on('change', function() {
|
||||
self._switchedRule();
|
||||
}).trigger('change');
|
||||
|
||||
self._find('[data-action="save"]').on('click', function() {
|
||||
self._trigger('save', self._getConfig());
|
||||
self.close();
|
||||
});
|
||||
|
||||
self._find('[data-action="cancel"]').on('click', function() {
|
||||
self.close();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the current competency can be configured.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @method canBeConfigured
|
||||
*/
|
||||
RuleConfig.prototype.canBeConfigured = function() {
|
||||
var can = false;
|
||||
$.each(this._rules, function(index, rule) {
|
||||
if (rule.canConfig()) {
|
||||
can = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return can;
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the dialogue.
|
||||
*
|
||||
* @method close
|
||||
*/
|
||||
RuleConfig.prototype.close = function() {
|
||||
this._popup.close();
|
||||
this._popup = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens the picker.
|
||||
*
|
||||
* @param {Number} competencyId The competency ID of the competency to work on.
|
||||
* @method display
|
||||
* @return {Promise}
|
||||
*/
|
||||
RuleConfig.prototype.display = function() {
|
||||
var self = this;
|
||||
if (!self._competency) {
|
||||
return;
|
||||
}
|
||||
return self._render().then(function(html) {
|
||||
return Str.get_string('competencyrule', 'tool_lp').then(function(title) {
|
||||
self._popup = new Dialogue(
|
||||
title,
|
||||
html,
|
||||
self._afterRender.bind(self)
|
||||
);
|
||||
});
|
||||
}).fail(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find a node in the dialogue.
|
||||
*
|
||||
* @param {String} selector
|
||||
* @method _find
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._find = function(selector) {
|
||||
return $(this._popup.getContent()).find(selector);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the applicable outcome options.
|
||||
*
|
||||
* @return {Array}
|
||||
* @method _getApplicableOutcomesOptions
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._getApplicableOutcomesOptions = function() {
|
||||
var self = this,
|
||||
options = [];
|
||||
|
||||
$.each(self._outcomesOption, function(index, outcome) {
|
||||
options.push({
|
||||
code: outcome.code,
|
||||
name: outcome.name,
|
||||
selected: (outcome.code == self._competency.ruleoutcome) ? true : false,
|
||||
});
|
||||
});
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the applicable rules options.
|
||||
*
|
||||
* @return {Array}
|
||||
* @method _getApplicableRulesOptions
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._getApplicableRulesOptions = function() {
|
||||
var self = this,
|
||||
options = [];
|
||||
|
||||
$.each(self._rules, function(index, rule) {
|
||||
if (!rule.canConfig()) {
|
||||
return;
|
||||
}
|
||||
options.push({
|
||||
name: self._getRuleName(rule.getType()),
|
||||
type: rule.getType(),
|
||||
selected: (rule.getType() == self._competency.ruletype) ? true : false,
|
||||
});
|
||||
});
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the full config for the competency.
|
||||
*
|
||||
* @return {Object} Contains rule, ruleoutcome and ruleconfig.
|
||||
* @method _getConfig
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._getConfig = function() {
|
||||
var rule = this._getRule();
|
||||
return {
|
||||
ruletype: rule ? rule.getType() : null,
|
||||
ruleconfig: rule ? rule.getConfig() : null,
|
||||
ruleoutcome: this._getOutcome()
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the selected outcome code.
|
||||
*
|
||||
* @return {String}
|
||||
* @method _getOutcome
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._getOutcome = function() {
|
||||
return this._find('[name="outcome"]').val();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the selected rule.
|
||||
*
|
||||
* @return {null|Rule}
|
||||
* @method _getRule
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._getRule = function() {
|
||||
var result,
|
||||
type = this._find('[name="rule"]').val();
|
||||
|
||||
$.each(this._rules, function(index, rule) {
|
||||
if (rule.getType() == type) {
|
||||
result = rule;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the name of a rule.
|
||||
*
|
||||
* @param {String} type The type of a rule.
|
||||
* @return {String}
|
||||
* @method _getRuleName
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._getRuleName = function(type) {
|
||||
var self = this,
|
||||
name;
|
||||
$.each(self._rulesModules, function(index, modInfo) {
|
||||
if (modInfo.type == type) {
|
||||
name = modInfo.name;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the outcomes.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @method _initOutcomes
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._initOutcomes = function() {
|
||||
var self = this;
|
||||
|
||||
return Outcomes.getAll().then(function(outcomes) {
|
||||
self._outcomesOption = outcomes;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the rules.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @method _initRules
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._initRules = function() {
|
||||
var self = this,
|
||||
promises = [];
|
||||
|
||||
$.each(self._rules, function(index, rule) {
|
||||
var promise = rule.init().then(function() {
|
||||
rule.setTargetCompetency(self._competency);
|
||||
rule.on('change', self._afterRuleConfigChange.bind(self));
|
||||
}, function() {
|
||||
// Upon failure remove the rule, and resolve the promise.
|
||||
self._rules.splice(index, 1);
|
||||
return $.when();
|
||||
});
|
||||
promises.push(promise);
|
||||
});
|
||||
|
||||
return $.when.apply($.when, promises);
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether or not the current config is valid.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @method _isValid
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._isValid = function() {
|
||||
var outcome = this._getOutcome(),
|
||||
rule = this._getRule();
|
||||
|
||||
if (outcome == Outcomes.NONE) {
|
||||
return true;
|
||||
} else if (!rule) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return rule.isValid();
|
||||
};
|
||||
|
||||
/**
|
||||
* Register an event listener.
|
||||
*
|
||||
* @param {String} type The event type.
|
||||
* @param {Function} handler The event listener.
|
||||
* @method on
|
||||
*/
|
||||
RuleConfig.prototype.on = function(type, handler) {
|
||||
this._eventNode.on(type, handler);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to executed before render.
|
||||
*
|
||||
* @method _preRender
|
||||
* @protected
|
||||
* @return {Promise}
|
||||
*/
|
||||
RuleConfig.prototype._preRender = function() {
|
||||
// We need to have all the information about the rule plugins first.
|
||||
return this.ready();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a promise that is resolved when the module is ready.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @method ready
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype.ready = function() {
|
||||
return this._ready.promise();
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the dialogue.
|
||||
*
|
||||
* @method _render
|
||||
* @protected
|
||||
* @return {Promise}
|
||||
*/
|
||||
RuleConfig.prototype._render = function() {
|
||||
var self = this;
|
||||
return this._preRender().then(function() {
|
||||
var config;
|
||||
|
||||
if (!self.canBeConfigured()) {
|
||||
config = false;
|
||||
} else {
|
||||
config = {};
|
||||
config.outcomes = self._getApplicableOutcomesOptions();
|
||||
config.rules = self._getApplicableRulesOptions();
|
||||
}
|
||||
|
||||
var context = {
|
||||
competencyshortname: self._competency.shortname,
|
||||
config: config
|
||||
};
|
||||
|
||||
return Templates.render('tool_lp/competency_rule_config', context);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the target competency.
|
||||
*
|
||||
* @param {Number} competencyId The target competency Id.
|
||||
* @method setTargetCompetencyId
|
||||
*/
|
||||
RuleConfig.prototype.setTargetCompetencyId = function(competencyId) {
|
||||
var self = this;
|
||||
self._competency = self._tree.getCompetency(competencyId);
|
||||
$.each(self._rules, function(index, rule) {
|
||||
rule.setTargetCompetency(self._competency);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Set up the instance.
|
||||
*
|
||||
* @return {Void}
|
||||
* @method _setUp
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._setUp = function() {
|
||||
var self = this,
|
||||
promises = [],
|
||||
modules = [];
|
||||
|
||||
self._ready = $.Deferred();
|
||||
self._rules = [];
|
||||
|
||||
$.each(self._rulesModules, function(index, rule) {
|
||||
modules.push(rule.amd);
|
||||
});
|
||||
|
||||
// Load all the modules.
|
||||
require(modules, function() {
|
||||
$.each(arguments, function(index, Module) {
|
||||
// Instantiate the rule and listen to it.
|
||||
var rule = new Module(self._tree);
|
||||
self._rules.push(rule);
|
||||
});
|
||||
|
||||
// Load all the option values.
|
||||
promises.push(self._initRules());
|
||||
promises.push(self._initOutcomes());
|
||||
|
||||
// Ready when everything is done.
|
||||
$.when.apply($.when, promises).always(function() {
|
||||
self._ready.resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the user switches outcome.
|
||||
*
|
||||
* @return {Void}
|
||||
* @method _switchedOutcome
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._switchedOutcome = function() {
|
||||
var self = this,
|
||||
type = self._getOutcome();
|
||||
|
||||
if (type == Outcomes.NONE) {
|
||||
// Reset to defaults.
|
||||
self._find('[data-region="rule-type"]').hide()
|
||||
.find('[name="rule"]').val(-1);
|
||||
self._find('[data-region="rule-config"]').empty().hide();
|
||||
self._afterChange();
|
||||
return;
|
||||
}
|
||||
|
||||
self._find('[data-region="rule-type"]').show();
|
||||
self._find('[data-region="rule-config"]').show();
|
||||
self._afterChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the user switches rule.
|
||||
*
|
||||
* @return {Void}
|
||||
* @method _switchedRule
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._switchedRule = function() {
|
||||
var self = this,
|
||||
container = self._find('[data-region="rule-config"]'),
|
||||
rule = self._getRule();
|
||||
|
||||
if (!rule) {
|
||||
container.empty().hide();
|
||||
self._afterChange();
|
||||
return;
|
||||
}
|
||||
|
||||
rule.injectTemplate(container).then(function() {
|
||||
container.show();
|
||||
}, function() {
|
||||
container.empty().hide();
|
||||
}).always(function() {
|
||||
self._afterChange();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger an event.
|
||||
*
|
||||
* @param {String} type The type of event.
|
||||
* @param {Object} The data to pass to the listeners.
|
||||
* @method _trigger
|
||||
* @protected
|
||||
*/
|
||||
RuleConfig.prototype._trigger = function(type, data) {
|
||||
this._eventNode.trigger(type, [data]);
|
||||
};
|
||||
|
||||
return /** @alias module:tool_lp/competencyruleconfig */ RuleConfig;
|
||||
|
||||
});
|
||||
@@ -26,7 +26,7 @@ define(['core/ajax', 'core/notification', 'core/templates', 'tool_lp/tree', 'jqu
|
||||
|
||||
// Private variables and functions.
|
||||
/** @var {Object[]} competencies - Cached list of competencies */
|
||||
var competencies = [];
|
||||
var competencies = {};
|
||||
|
||||
/** @var {Number} competencyFrameworkId - The current framework id */
|
||||
var competencyFrameworkId = 0;
|
||||
@@ -160,6 +160,23 @@ define(['core/ajax', 'core/notification', 'core/templates', 'tool_lp/tree', 'jqu
|
||||
$(treeSelector).on(eventname, handler);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the children of a competency.
|
||||
*
|
||||
* @param {Number} id The competency ID.
|
||||
* @return {Array}
|
||||
* @method getChildren
|
||||
*/
|
||||
getChildren: function(id) {
|
||||
var children = [];
|
||||
$.each(competencies, function(index, competency) {
|
||||
if (competency.parentid == id) {
|
||||
children.push(competency);
|
||||
}
|
||||
});
|
||||
return children;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the competency framework id this model was initiliased with.
|
||||
*
|
||||
@@ -191,6 +208,17 @@ define(['core/ajax', 'core/notification', 'core/templates', 'tool_lp/tree', 'jqu
|
||||
return level;
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether a competency has children.
|
||||
*
|
||||
* @param {Number} id The competency ID.
|
||||
* @return {Boolean}
|
||||
* @method hasChildren
|
||||
*/
|
||||
hasChildren: function(id) {
|
||||
return this.getChildren(id).length > 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reload all the page competencies framework competencies.
|
||||
* @method reloadCompetencies
|
||||
|
||||
@@ -209,17 +209,17 @@ class api {
|
||||
* @return boolean
|
||||
*/
|
||||
public static function update_competency($record) {
|
||||
$current = new competency($record->id);
|
||||
$competency = new competency($record->id);
|
||||
|
||||
// First we do a permissions check.
|
||||
require_capability('tool/lp:competencymanage', $current->get_framework()->get_context());
|
||||
require_capability('tool/lp:competencymanage', $competency->get_framework()->get_context());
|
||||
|
||||
// Some things should not be changed in an update - they should use a more specific method.
|
||||
$record->sortorder = $current->get_sortorder();
|
||||
$record->parentid = $current->get_parentid();
|
||||
$record->competencyframeworkid = $current->get_competencyframeworkid();
|
||||
$record->sortorder = $competency->get_sortorder();
|
||||
$record->parentid = $competency->get_parentid();
|
||||
$record->competencyframeworkid = $competency->get_competencyframeworkid();
|
||||
|
||||
$competency = new competency(0, $record);
|
||||
$competency->from_record($record);
|
||||
require_capability('tool/lp:competencymanage', $competency->get_framework()->get_context());
|
||||
|
||||
// OK - all set.
|
||||
|
||||
@@ -37,6 +37,15 @@ class competency extends persistent {
|
||||
|
||||
const TABLE = 'tool_lp_competency';
|
||||
|
||||
/** Outcome none. */
|
||||
const OUTCOME_NONE = 0;
|
||||
/** Outcome evidence. */
|
||||
const OUTCOME_EVIDENCE = 1;
|
||||
/** Outcome complete. */
|
||||
const OUTCOME_COMPLETE = 2;
|
||||
/** Outcome recommend. */
|
||||
const OUTCOME_RECOMMEND = 3;
|
||||
|
||||
/** @var competency Object before update. */
|
||||
protected $beforeupdate = null;
|
||||
|
||||
@@ -78,6 +87,21 @@ class competency extends persistent {
|
||||
'default' => '/0/',
|
||||
'type' => PARAM_RAW
|
||||
),
|
||||
'ruleoutcome' => array(
|
||||
'choices' => array(self::OUTCOME_NONE, self::OUTCOME_EVIDENCE, self::OUTCOME_COMPLETE, self::OUTCOME_RECOMMEND),
|
||||
'default' => self::OUTCOME_NONE,
|
||||
'type' => PARAM_INT
|
||||
),
|
||||
'ruletype' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'default' => null,
|
||||
'null' => NULL_ALLOWED
|
||||
),
|
||||
'ruleconfig' => array(
|
||||
'default' => null,
|
||||
'type' => PARAM_RAW,
|
||||
'null' => NULL_ALLOWED
|
||||
),
|
||||
'competencyframeworkid' => array(
|
||||
'default' => 0,
|
||||
'type' => PARAM_INT
|
||||
@@ -215,6 +239,37 @@ class competency extends persistent {
|
||||
return related_competency::get_related_competencies($this->get_id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rule object.
|
||||
*
|
||||
* @return null|competency_rule
|
||||
*/
|
||||
public function get_rule_object() {
|
||||
$rule = $this->get_ruletype();
|
||||
|
||||
if (!$rule || !is_subclass_of($rule, '\tool_lp\competency_rule')) {
|
||||
// Double check that the rule is extending the right class to avoid bad surprises.
|
||||
return null;
|
||||
}
|
||||
|
||||
return new $rule($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the competency is the parent of passed competencies.
|
||||
*
|
||||
* @param array $ids IDs of supposedly direct children.
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_parent_of(array $ids) {
|
||||
global $DB;
|
||||
|
||||
list($insql, $params) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
|
||||
$params['parentid'] = $this->get_id();
|
||||
|
||||
return $DB->count_records_select(self::TABLE, "id $insql AND parentid = :parentid", $params) == count($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to set the path.
|
||||
*
|
||||
@@ -404,6 +459,53 @@ class competency extends persistent {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the rule.
|
||||
*
|
||||
* @param string $value The ID.
|
||||
* @return true|lang_string
|
||||
*/
|
||||
protected function validate_ruletype($value) {
|
||||
if ($value === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!class_exists($value) || !is_subclass_of($value, '\tool_lp\competency_rule')) {
|
||||
return new lang_string('invaliddata', 'error');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the rule config.
|
||||
*
|
||||
* @param string $value The ID.
|
||||
* @return true|lang_string
|
||||
*/
|
||||
protected function validate_ruleconfig($value) {
|
||||
$rule = $this->get_rule_object();
|
||||
|
||||
// We don't have a rule.
|
||||
if (empty($rule)) {
|
||||
if ($value === null) {
|
||||
// No config, perfect.
|
||||
return true;
|
||||
} else if (empty($rule) && !$value !== null) {
|
||||
// Config but no rules, whoops!
|
||||
return new lang_string('invaliddata', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
$valid = $rule->validate_config($value);
|
||||
if ($valid !== true) {
|
||||
// Whoops!
|
||||
return new lang_string('invaliddata', 'error');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether or not the competency IDs share the same framework.
|
||||
*
|
||||
@@ -416,6 +518,24 @@ class competency extends persistent {
|
||||
return $DB->count_records_select(self::TABLE, "id $insql", $params, "COUNT(DISTINCT(competencyframeworkid))") == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the available rules.
|
||||
*
|
||||
* @return array Keys are the class names, values is an object containing name and amd.
|
||||
*/
|
||||
public static function get_available_rules() {
|
||||
// Fully qualified class names withough leading slashes because get_class() does not add them either.
|
||||
$rules = array(
|
||||
'tool_lp\competency_rule_all' => (object) array(),
|
||||
'tool_lp\competency_rule_points' => (object) array(),
|
||||
);
|
||||
foreach ($rules as $class => $rule) {
|
||||
$rule->name = $class::get_name();
|
||||
$rule->amd = $class::get_amd_module();
|
||||
}
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a framework tree with competency nodes.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Competency rule base.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_lp;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use coding_exception;
|
||||
|
||||
/**
|
||||
* Competency rule base abstract class.
|
||||
*
|
||||
* Rules are attached to a competency and then tested against a user competency
|
||||
* to determine whether or not it matches.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class competency_rule {
|
||||
|
||||
/** @var competency The competency. */
|
||||
protected $competency;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param competency $competency The competency.
|
||||
*/
|
||||
public function __construct(competency $competency) {
|
||||
$class = $competency->get_ruletype();
|
||||
if (!$class || !($this instanceof $class)) {
|
||||
throw new coding_exception('This competency does not use this rule.');
|
||||
}
|
||||
|
||||
$this->competency = $competency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rule config.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function get_config() {
|
||||
return $this->competency->get_ruleconfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the rule is matched.
|
||||
*
|
||||
* @param user_competency $usercompetency The user competency to test against.
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function matches(user_competency $usercompetency);
|
||||
|
||||
/**
|
||||
* Validate the rule config.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function validate_config($value);
|
||||
|
||||
/**
|
||||
* Get the fully qualified name of the AMD module to use.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_amd_module() {
|
||||
throw new coding_exception('Method not implemented.');
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the rule.
|
||||
*
|
||||
* @return lang_string
|
||||
*/
|
||||
public static function get_name() {
|
||||
throw new coding_exception('Method not implemented.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Competency rule all.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_lp;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use lang_string;
|
||||
|
||||
/**
|
||||
* Competency rule all class.
|
||||
*
|
||||
* This rule is considered matched when all the children of a competency are completed.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class competency_rule_all extends competency_rule {
|
||||
|
||||
/**
|
||||
* Whether or not the rule is matched.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function matches(user_competency $usercompetency) {
|
||||
global $DB;
|
||||
|
||||
// TODO Improve performance here, perhaps the caller could already provide records.
|
||||
$children = competency::get_records(array('parentid' => $usercompetency->get_competencyid()));
|
||||
|
||||
if (empty($children)) {
|
||||
// Leaves are not compatible with this rule.
|
||||
return false;
|
||||
}
|
||||
|
||||
$ids = array();
|
||||
foreach ($children as $child) {
|
||||
$ids[] = $child->get_id();
|
||||
}
|
||||
|
||||
list($insql, $params) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
|
||||
$sql = "userid = :userid
|
||||
AND proficiency = :proficiency
|
||||
AND competencyid $insql";
|
||||
$params['userid'] = $usercompetency->get_userid();
|
||||
$params['proficiency'] = 1;
|
||||
|
||||
// Is the user is marked as proficient in all children?
|
||||
return user_competency::count_records_select($sql, $params) === count($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the rule config.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validate_config($value) {
|
||||
return $value === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the rule config.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function get_amd_module() {
|
||||
return 'tool_lp/competency_rule_all';
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the rule.
|
||||
*
|
||||
* @return lang_string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return new lang_string('allchildrenarecomplete', 'tool_lp');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Competency rule points based.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_lp;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use lang_string;
|
||||
|
||||
|
||||
/**
|
||||
* Competency rule points based class.
|
||||
*
|
||||
* This rule matches when related competencies contribute for a required number of points.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class competency_rule_points extends competency_rule {
|
||||
|
||||
/**
|
||||
* Get the rule config.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function get_config() {
|
||||
$config = parent::get_config();
|
||||
return json_decode($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the rule is matched.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function matches(user_competency $usercompetency) {
|
||||
global $DB;
|
||||
|
||||
$config = $this->get_config();
|
||||
$pointsrequired = $config->base->points;
|
||||
|
||||
// Index by competency ID and extract required.
|
||||
$compsrules = array();
|
||||
$requiredids = array();
|
||||
foreach ($config->competencies as $comp) {
|
||||
$compsrules[$comp->id] = $comp;
|
||||
if ($comp->required) {
|
||||
$requiredids[$comp->id] = $comp->id;
|
||||
}
|
||||
}
|
||||
|
||||
// Find all the user competency records.
|
||||
list($insql, $params) = $DB->get_in_or_equal(array_keys($compsrules), SQL_PARAMS_NAMED);
|
||||
$sql = "userid = :userid
|
||||
AND proficiency = :proficiency
|
||||
AND competencyid $insql";
|
||||
$params['userid'] = $usercompetency->get_userid();
|
||||
$params['proficiency'] = 1;
|
||||
$ucs = user_competency::get_records_select($sql, $params, '', 'competencyid');
|
||||
|
||||
// Check that all the required are found.
|
||||
if (!empty($requiredids)) {
|
||||
$unmetrequired = array_diff_key($requiredids, $ucs);
|
||||
if (!empty($unmetrequired)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we have enough points.
|
||||
$points = 0;
|
||||
foreach ($compsrules as $compid => $comp) {
|
||||
if (array_key_exists($compid, $ucs)) {
|
||||
$points += $comp->points;
|
||||
}
|
||||
}
|
||||
|
||||
return $points >= $pointsrequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the rule config.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validate_config($value) {
|
||||
$compids = array();
|
||||
$config = json_decode($value);
|
||||
if ($config === null || !isset($config->base) || !isset($config->competencies)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($config->base->points)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$requiredpoints = validate_param($config->base->points, PARAM_INT);
|
||||
} catch (\invalid_parameter_exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($requiredpoints < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$totalpoints = 0;
|
||||
|
||||
// Validate the competency info.
|
||||
foreach ($config->competencies as $competency) {
|
||||
|
||||
// Cannot include self.
|
||||
if ($competency->id == $this->competency->get_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for duplicates.
|
||||
if (in_array($competency->id, $compids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for required fields.
|
||||
if (!isset($competency->id)
|
||||
|| !isset($competency->points)
|
||||
|| !isset($competency->required)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate the parameters.
|
||||
try {
|
||||
validate_param($competency->id, PARAM_INT);
|
||||
$points = validate_param($competency->points, PARAM_INT);
|
||||
validate_param($competency->required, PARAM_BOOL);
|
||||
} catch (\invalid_parameter_exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$totalpoints += $points;
|
||||
if ($points < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$compids[] = $competency->id;
|
||||
}
|
||||
|
||||
// No competencies, that's strange.
|
||||
if (empty($compids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Impossible to reach the points required.
|
||||
if ($requiredpoints > $totalpoints) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that all the competencies are children of the competency.
|
||||
// We may want to relax this check at a later stage if we want to allow competencies
|
||||
// to be linked throughout the whole framework.
|
||||
return $this->competency->is_parent_of($compids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the rule config.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function get_amd_module() {
|
||||
return 'tool_lp/competency_rule_points';
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the rule.
|
||||
*
|
||||
* @return lang_string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return new lang_string('pointsrequiredaremet', 'tool_lp');
|
||||
}
|
||||
}
|
||||
@@ -1247,6 +1247,24 @@ class external extends external_api {
|
||||
'Is this framework visible?',
|
||||
VALUE_REQUIRED
|
||||
);
|
||||
$ruletype = new external_value(
|
||||
PARAM_RAW,
|
||||
'The type of rule',
|
||||
VALUE_DEFAULT,
|
||||
null
|
||||
);
|
||||
$ruleoutcome = new external_value(
|
||||
PARAM_INT,
|
||||
'The outcome when the rule matches. Constant value competency::OUTCOME_*',
|
||||
VALUE_DEFAULT,
|
||||
competency::OUTCOME_NONE
|
||||
);
|
||||
$ruleconfig = new external_value(
|
||||
PARAM_RAW,
|
||||
'The extra config of the rule.',
|
||||
VALUE_DEFAULT,
|
||||
null
|
||||
);
|
||||
|
||||
$params = array(
|
||||
'id' => $id,
|
||||
@@ -1255,6 +1273,9 @@ class external extends external_api {
|
||||
'description' => $description,
|
||||
'descriptionformat' => $descriptionformat,
|
||||
'visible' => $visible,
|
||||
'ruletype' => $ruletype,
|
||||
'ruleoutcome' => $ruleoutcome,
|
||||
'ruleconfig' => $ruleconfig,
|
||||
);
|
||||
return new external_function_parameters($params);
|
||||
}
|
||||
@@ -1275,7 +1296,10 @@ class external extends external_api {
|
||||
$idnumber,
|
||||
$description,
|
||||
$descriptionformat,
|
||||
$visible) {
|
||||
$visible,
|
||||
$ruletype = null,
|
||||
$ruleoutcome = competency::OUTCOME_NONE,
|
||||
$ruleconfig = null) {
|
||||
|
||||
$params = self::validate_parameters(self::update_competency_parameters(),
|
||||
array(
|
||||
@@ -1284,7 +1308,10 @@ class external extends external_api {
|
||||
'idnumber' => $idnumber,
|
||||
'description' => $description,
|
||||
'descriptionformat' => $descriptionformat,
|
||||
'visible' => $visible
|
||||
'visible' => $visible,
|
||||
'ruletype' => $ruletype,
|
||||
'ruleoutcome' => $ruleoutcome,
|
||||
'ruleconfig' => $ruleconfig,
|
||||
));
|
||||
$params = (object) $params;
|
||||
|
||||
@@ -1555,7 +1582,12 @@ class external extends external_api {
|
||||
'framework' => self::get_competency_framework_external_structure(),
|
||||
'canmanage' => new external_value(PARAM_BOOL, 'True if this user has permission to manage competency frameworks'),
|
||||
'pagecontextid' => new external_value(PARAM_INT, 'Context id for the framework'),
|
||||
'search' => new external_value(PARAM_RAW, 'Current search string')
|
||||
'search' => new external_value(PARAM_RAW, 'Current search string'),
|
||||
'rulesmodules' => new external_multiple_structure(new external_single_structure(array(
|
||||
'type' => new external_value(PARAM_RAW, 'The rule type'),
|
||||
'amd' => new external_value(PARAM_RAW, 'The AMD module of the rule'),
|
||||
'name' => new external_value(PARAM_TEXT, 'The name of the rule'),
|
||||
)))
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ use stdClass;
|
||||
use moodle_url;
|
||||
use context_system;
|
||||
use tool_lp\api;
|
||||
use tool_lp\competency;
|
||||
use tool_lp\competency_framework;
|
||||
|
||||
/**
|
||||
@@ -119,6 +120,13 @@ class manage_competencies_page implements renderable, templatable {
|
||||
$data->search = $this->search;
|
||||
$data->pagecontextid = $this->pagecontext->id;
|
||||
|
||||
$rules = competency::get_available_rules();
|
||||
foreach ($rules as $type => $rule) {
|
||||
$rule->name = (string) $rule->name;
|
||||
$rule->type = $type;
|
||||
}
|
||||
$data->rulesmodules = json_encode(array_values($rules));
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,8 +680,8 @@ abstract class persistent {
|
||||
|
||||
// We return class instances.
|
||||
$instances = array();
|
||||
foreach ($records as $record) {
|
||||
array_push($instances, new static(0, $record));
|
||||
foreach ($records as $key => $record) {
|
||||
$instances[$key] = new static(0, $record);
|
||||
}
|
||||
|
||||
return $instances;
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
<FIELD NAME="visible" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Used to hide the competency."/>
|
||||
<FIELD NAME="path" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="Used to speed up queries that use an entire branch of the tree. Looks like /5/34/54."/>
|
||||
<FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Relative sort order within the branch"/>
|
||||
<FIELD NAME="ruletype" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="ruleoutcome" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="ruleconfig" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The time this competency was created."/>
|
||||
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The time this competency was last modified."/>
|
||||
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="The user who last modified this competency"/>
|
||||
@@ -25,6 +28,7 @@
|
||||
</KEYS>
|
||||
<INDEXES>
|
||||
<INDEX NAME="idnumberframework" UNIQUE="true" FIELDS="competencyframeworkid, idnumber"/>
|
||||
<INDEX NAME="ruleoutcome" UNIQUE="false" FIELDS="ruleoutcome"/>
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
<TABLE NAME="tool_lp_competency_framework" COMMENT="List of competency frameworks.">
|
||||
@@ -134,7 +138,7 @@
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="tool_lp_user_competency" COMMENT="User competencies">
|
||||
<TABLE NAME="tool_lp_user_competency" COMMENT="User competencies">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="User associated to the competency."/>
|
||||
|
||||
@@ -310,5 +310,65 @@ function xmldb_tool_lp_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2015052427, 'tool', 'lp');
|
||||
}
|
||||
|
||||
if ($oldversion < 2015111001) {
|
||||
|
||||
// Define field ruletype to be added to tool_lp_competency.
|
||||
$table = new xmldb_table('tool_lp_competency');
|
||||
$field = new xmldb_field('ruletype', XMLDB_TYPE_CHAR, '100', null, null, null, null, 'sortorder');
|
||||
|
||||
// Conditionally launch add field ruletype.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Lp savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2015111001, 'tool', 'lp');
|
||||
}
|
||||
|
||||
if ($oldversion < 2015111002) {
|
||||
|
||||
// Define field ruleoutcome to be added to tool_lp_competency.
|
||||
$table = new xmldb_table('tool_lp_competency');
|
||||
$field = new xmldb_field('ruleoutcome', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'ruletype');
|
||||
|
||||
// Conditionally launch add field ruleoutcome.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Lp savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2015111002, 'tool', 'lp');
|
||||
}
|
||||
|
||||
if ($oldversion < 2015111003) {
|
||||
|
||||
// Define field ruleconfig to be added to tool_lp_competency.
|
||||
$table = new xmldb_table('tool_lp_competency');
|
||||
$field = new xmldb_field('ruleconfig', XMLDB_TYPE_TEXT, null, null, null, null, null, 'ruleoutcome');
|
||||
|
||||
// Conditionally launch add field ruleconfig.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Lp savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2015111003, 'tool', 'lp');
|
||||
}
|
||||
|
||||
if ($oldversion < 2015111004) {
|
||||
|
||||
// Define index ruleoutcome (not unique) to be added to tool_lp_competency.
|
||||
$table = new xmldb_table('tool_lp_competency');
|
||||
$index = new xmldb_index('ruleoutcome', XMLDB_INDEX_NOTUNIQUE, array('ruleoutcome'));
|
||||
|
||||
// Conditionally launch add index ruleoutcome.
|
||||
if (!$dbman->index_exists($table, $index)) {
|
||||
$dbman->add_index($table, $index);
|
||||
}
|
||||
|
||||
// Lp savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2015111004, 'tool', 'lp');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ $string['addnewcompetencyframework'] = 'Add new competency framework';
|
||||
$string['addnewplan'] = 'Add new learning plan';
|
||||
$string['addnewtemplate'] = 'Add new learning plan template';
|
||||
$string['addrelatedcompetency'] = 'Add related competency';
|
||||
$string['allchildrenarecomplete'] = 'All children are complete';
|
||||
$string['cannotaddrules'] = 'This competency cannot be configured.';
|
||||
$string['competencies'] = 'Competencies';
|
||||
$string['competenciesforframework'] = 'Competencies for {$a}';
|
||||
$string['competencycreated'] = 'Competency created';
|
||||
@@ -37,8 +39,13 @@ $string['competencyframeworkcreated'] = 'Competency framework created.';
|
||||
$string['competencyframeworkname'] = 'Name';
|
||||
$string['competencyframeworks'] = 'Competency Frameworks';
|
||||
$string['competencyframeworkupdated'] = 'Competency framework updated.';
|
||||
$string['competencyoutcome_complete'] = 'Mark as complete';
|
||||
$string['competencyoutcome_evidence'] = 'Attach an evidence';
|
||||
$string['competencyoutcome_none'] = 'None';
|
||||
$string['competencyoutcome_recommend'] = 'Recommend the competency';
|
||||
$string['competencypicker'] = 'Competency picker';
|
||||
$string['competencyrelatedcompetencies'] = '{$a} related competencies';
|
||||
$string['competencyrule'] = 'Competency rule';
|
||||
$string['competencyupdated'] = 'Competency updated';
|
||||
$string['configurescale'] = 'Configure scales';
|
||||
$string['coursecompetencies'] = 'Course competencies';
|
||||
@@ -72,6 +79,7 @@ $string['invalidpersistent'] = 'Invalid persistent';
|
||||
$string['invalidplan'] = 'Invalid learning plan';
|
||||
$string['invalidtaxonomy'] = 'Invalid taxonomy: {$a}';
|
||||
$string['itemstoadd'] = 'Items to add';
|
||||
$string['aisrequired'] = '\'{$a}\' is required';
|
||||
$string['learningplans'] = 'Learning plans';
|
||||
$string['levela'] = 'Level {$a}';
|
||||
$string['linkcoursecompetencies'] = 'Link course competencies';
|
||||
@@ -112,6 +120,7 @@ $string['nolinkedcourses'] = 'No courses are using this competency';
|
||||
$string['norelatedcompetencies'] = 'No other competencies have been related to this competency.';
|
||||
$string['notemplates'] = 'No learning plan templates have been created yet.';
|
||||
$string['nouserplans'] = 'No learning plans have been created yet.';
|
||||
$string['outcome'] = 'Outcome';
|
||||
$string['parentcompetency'] = 'Parent competency';
|
||||
$string['plancreated'] = 'Learning plan created';
|
||||
$string['plandescription'] = 'Description';
|
||||
@@ -123,6 +132,9 @@ $string['plantemplate'] = 'Select template';
|
||||
$string['plantemplate_help'] = 'A learning plan created from a template will contain a list of competencies that match the template. Updates to the template will be reflected in any plan created from that template.';
|
||||
$string['planupdated'] = 'Learning plan updated';
|
||||
$string['pluginname'] = 'Learning Plans';
|
||||
$string['points'] = 'Points';
|
||||
$string['pointsgivenfor'] = 'Points given for \'{$a}\'';
|
||||
$string['pointsrequiredaremet'] = 'Points required are met';
|
||||
$string['proficient'] = 'Proficient';
|
||||
$string['relatedcompetencies'] = 'Related competencies';
|
||||
$string['savechanges'] = 'Save changes';
|
||||
@@ -196,8 +208,10 @@ $string['templatecreated'] = 'Learning plan template created';
|
||||
$string['templatename'] = 'Name';
|
||||
$string['templates'] = 'Learning plan templates';
|
||||
$string['templateupdated'] = 'Learning plan template updated';
|
||||
$string['totalrequiredtocomplete'] = 'Total required to complete';
|
||||
$string['usercompetencystatus_idle'] = 'Idle';
|
||||
$string['usercompetencystatus_inreview'] = 'In review';
|
||||
$string['usercompetencystatus_waitingforreview'] = 'Waiting for review';
|
||||
$string['visible'] = 'Visible';
|
||||
$string['visible_help'] = 'A competency framework can be hidden from teachers. This could be useful if a framework is still in the process of being developed.';
|
||||
$string['when'] = 'When';
|
||||
|
||||
@@ -52,6 +52,47 @@
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-base"] {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-outcome"],
|
||||
.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-type"] {
|
||||
display: table-row;
|
||||
}
|
||||
.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-outcome"] label,
|
||||
.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-type"] label {
|
||||
padding-right: 10px;
|
||||
}
|
||||
.dir-rtl.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-outcome"] label,
|
||||
.dir-rtl.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-type"] label {
|
||||
padding-left: 10px;
|
||||
padding-right: 0;
|
||||
}
|
||||
.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-outcome"] label,
|
||||
.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-outcome"] select,
|
||||
.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-type"] label,
|
||||
.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-type"] select {
|
||||
display: table-cell;
|
||||
}
|
||||
.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-outcome"] select,
|
||||
.path-admin-tool-lp [data-region="competencyruleconfig"] [data-region="rule-type"] select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.path-admin-tool-lp .competency-rule-points {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.path-admin-tool-lp .competency-rule-points table input {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.path-admin-tool-lp .competency-rule-points tr[data-competency] th {
|
||||
font-weight: normal;
|
||||
}
|
||||
.path-admin-tool-lp .competency-rule-points input[type="number"] {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
/** Styles required for menubar js */
|
||||
|
||||
.tool-lp-menu li {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<div data-region="competencyruleconfig">
|
||||
<div data-region="content">
|
||||
|
||||
{{^config}}
|
||||
<p>{{#str}}cannotaddrules, tool_lp{{/str}}</p>
|
||||
{{/config}}
|
||||
|
||||
{{#config}}
|
||||
<div data-region="rule-base">
|
||||
<div data-region="rule-outcome">
|
||||
<label>{{#str}}outcome, tool_lp{{/str}}</label>
|
||||
<select name="outcome" ng-label="{{#str}}outcome, tool_lp{{/str}}">
|
||||
{{#outcomes}}
|
||||
<option value="{{code}}" {{#selected}}selected{{/selected}}>{{name}}</option>
|
||||
{{/outcomes}}
|
||||
</select>
|
||||
</div>
|
||||
<div data-region="rule-type">
|
||||
<label>{{#str}}when, tool_lp{{/str}}</label>
|
||||
<select name="rule" ng-label="{{#str}}when, tool_lp{{/str}}">
|
||||
<option value="-1">{{#str}}choosedots{{/str}}</option>
|
||||
{{#rules}}
|
||||
<option value="{{type}}" {{#selected}}selected{{/selected}}>{{name}}</option>
|
||||
{{/rules}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div data-region="rule-config">
|
||||
</div>
|
||||
{{/config}}
|
||||
</div>
|
||||
|
||||
<div data-region="footer">
|
||||
{{#config}}
|
||||
<input type="button" data-action="save" value="{{#str}}savechanges{{/str}}"/>
|
||||
{{/config}}
|
||||
<input type="button" data-action="cancel" value="{{#str}}cancel{{/str}}"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,27 @@
|
||||
<div class="competency-rule-points">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col"></th>
|
||||
<th scope="col">{{#str}}points, tool_lp{{/str}}</th>
|
||||
<th scope="col">{{#str}}required{{/str}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#children}}
|
||||
<tr data-competency="{{id}}">
|
||||
<th scope="row">{{shortname}}</th>
|
||||
<td><input type="number" min="0" value="{{points}}" name="points" aria-label="{{#str}}pointsgivenfor, tool_lp, {{competency.shortname}}{{/str}}"></td>
|
||||
<td><input type="checkbox" value="1" name="required" {{#required}}checked{{/required}} aria-label="{{#str}}aisrequired, tool_lp, {{competency.shortname}}{{/str}}"></td>
|
||||
</tr>
|
||||
{{/children}}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th scope="row">{{#str}}totalrequiredtocomplete, tool_lp{{/str}}</th>
|
||||
<td><input type="number" min="1" value="{{requiredpoints}}" name="requiredpoints" aria-label="{{#str}}totalrequiredtocomplete, tool_lp{{/str}}"></td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
@@ -6,3 +6,12 @@
|
||||
{{#showrelatedcompetencies}}
|
||||
{{> tool_lp/related_competencies }}
|
||||
{{/showrelatedcompetencies}}
|
||||
{{#showrule}}
|
||||
<h5>{{#str}}competencyrule, tool_lp{{/str}}</h5>
|
||||
<dl>
|
||||
<dt>{{#str}}outcome, tool_lp{{/str}}</dt>
|
||||
<dd>{{rule.outcome}}</dd>
|
||||
<dt>{{#str}}when, tool_lp{{/str}}</dt>
|
||||
<dd>{{rule.type}}</dd>
|
||||
</dl>
|
||||
{{/showrule}}
|
||||
|
||||
@@ -94,6 +94,11 @@
|
||||
{{#pix}}t/add{{/pix}} {{#str}}addrelatedcompetency, tool_lp{{/str}}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" data-action="competencyrules">
|
||||
{{#pix}}t/edit{{/pix}} {{#str}}competencyrule, tool_lp{{/str}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -113,7 +118,7 @@ require(['tool_lp/tree', 'tool_lp/competencytree', 'tool_lp/competencyactions',
|
||||
'{{search}}',
|
||||
'[data-enhance=tree]');
|
||||
|
||||
actions.init(treeModel, {{pagecontextid}}, {{{framework.taxonomies}}});
|
||||
actions.init(treeModel, {{pagecontextid}}, {{{framework.taxonomies}}}, {{{rulesmodules}}});
|
||||
|
||||
});
|
||||
{{/js}}
|
||||
|
||||
@@ -221,24 +221,22 @@ class tool_lp_api_testcase extends advanced_testcase {
|
||||
// Get frameworks list order by shortname desc.
|
||||
$result = api::list_frameworks('shortname', 'DESC', null, 3, context_system::instance());
|
||||
|
||||
$r1 = (object) $result[0];
|
||||
$r2 = (object) $result[1];
|
||||
$r3 = (object) $result[2];
|
||||
|
||||
$this->assertEquals($framework1->get_id(), $r3->get_id());
|
||||
$this->assertEquals($framework2->get_id(), $r2->get_id());
|
||||
$this->assertEquals($framework3->get_id(), $r1->get_id());
|
||||
$f = (object) array_shift($result);
|
||||
$this->assertEquals($framework3->get_id(), $f->get_id());
|
||||
$f = (object) array_shift($result);
|
||||
$this->assertEquals($framework2->get_id(), $f->get_id());
|
||||
$f = (object) array_shift($result);
|
||||
$this->assertEquals($framework1->get_id(), $f->get_id());
|
||||
|
||||
// Get frameworks list order by idnumber asc.
|
||||
$result = api::list_frameworks('idnumber', 'ASC', null, 3, context_system::instance());
|
||||
|
||||
$r1 = (object) $result[0];
|
||||
$r2 = (object) $result[1];
|
||||
$r3 = (object) $result[2];
|
||||
|
||||
$this->assertEquals($framework1->get_id(), $r3->get_id());
|
||||
$this->assertEquals($framework2->get_id(), $r1->get_id());
|
||||
$this->assertEquals($framework3->get_id(), $r2->get_id());
|
||||
$f = (object) array_shift($result);
|
||||
$this->assertEquals($framework2->get_id(), $f->get_id());
|
||||
$f = (object) array_shift($result);
|
||||
$this->assertEquals($framework3->get_id(), $f->get_id());
|
||||
$f = (object) array_shift($result);
|
||||
$this->assertEquals($framework1->get_id(), $f->get_id());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Competency rule tests.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
|
||||
|
||||
use tool_lp\user_competency;
|
||||
use tool_lp\competency;
|
||||
use tool_lp\competency_rule_all;
|
||||
use tool_lp\competency_rule_points;
|
||||
|
||||
/**
|
||||
* Competency rule testcase.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2015 Frédéric Massart - FMCorz.net
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_lp_competency_rule_testcase extends externallib_advanced_testcase {
|
||||
|
||||
public function test_rule_all_matching() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$this->setAdminUser();
|
||||
$lpg = $this->getDataGenerator()->get_plugin_generator('tool_lp');
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
|
||||
// Set up the framework and competencies.
|
||||
$framework = $lpg->create_framework();
|
||||
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(),
|
||||
'ruletype' => 'tool_lp\competency_rule_all'));
|
||||
$c11 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(), 'parentid' => $c1->get_id(),
|
||||
'ruletype' => 'tool_lp\competency_rule_all'));
|
||||
$c111 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(), 'parentid' => $c11->get_id(),
|
||||
'ruletype' => 'tool_lp\competency_rule_all'));
|
||||
$c112 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(), 'parentid' => $c11->get_id(),
|
||||
'ruletype' => 'tool_lp\competency_rule_all'));
|
||||
$c12 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(), 'parentid' => $c1->get_id(),
|
||||
'ruletype' => 'tool_lp\competency_rule_all'));
|
||||
$c13 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(), 'parentid' => $c1->get_id(),
|
||||
'ruletype' => 'tool_lp\competency_rule_all'));
|
||||
$c131 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(), 'parentid' => $c13->get_id(),
|
||||
'ruletype' => 'tool_lp\competency_rule_all'));
|
||||
|
||||
// Create some user competency records.
|
||||
$uc1 = $lpg->create_user_competency(array('competencyid' => $c1->get_id(), 'userid' => $u1->id));
|
||||
$uc11 = $lpg->create_user_competency(array('competencyid' => $c11->get_id(), 'userid' => $u1->id, 'proficiency' => 1));
|
||||
$uc111 = $lpg->create_user_competency(array('competencyid' => $c111->get_id(), 'userid' => $u1->id, 'proficiency' => 1));
|
||||
$uc112 = $lpg->create_user_competency(array('competencyid' => $c112->get_id(), 'userid' => $u1->id, 'proficiency' => 1));
|
||||
$uc12 = $lpg->create_user_competency(array('competencyid' => $c12->get_id(), 'userid' => $u1->id));
|
||||
$uc13 = new user_competency(0, (object) array('userid' => $u1->id, 'competencyid' => $c13->get_id()));
|
||||
|
||||
// Not all children are met.
|
||||
$cr = new competency_rule_all($c1);
|
||||
$this->assertFalse($cr->matches($uc1));
|
||||
|
||||
// All children are met.
|
||||
$cr = new competency_rule_all($c11);
|
||||
$this->assertTrue($cr->matches($uc11));
|
||||
|
||||
// The competency doesn't have any children.
|
||||
$cr = new competency_rule_all($c12);
|
||||
$this->assertFalse($cr->matches($uc12));
|
||||
|
||||
// The competency doesn't have saved user competency records.
|
||||
$cr = new competency_rule_all($c13);
|
||||
$this->assertFalse($cr->matches($uc13));
|
||||
}
|
||||
|
||||
public function test_rule_points_validation() {
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
|
||||
$lpg = $this->getDataGenerator()->get_plugin_generator('tool_lp');
|
||||
$framework = $lpg->create_framework();
|
||||
$framework2 = $lpg->create_framework();
|
||||
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id()));
|
||||
$c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(), 'parentid' => $c1->get_id()));
|
||||
$c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(), 'parentid' => $c1->get_id()));
|
||||
$c4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id()));
|
||||
$cx = $lpg->create_competency(array('competencyframeworkid' => $framework2->get_id()));
|
||||
|
||||
$c1->set_ruletype('tool_lp\competency_rule_points');
|
||||
$rule = new competency_rule_points($c1);
|
||||
|
||||
// Invalid config.
|
||||
$config = json_encode(array());
|
||||
$this->assertFalse($rule->validate_config($config));
|
||||
|
||||
// Missing required points.
|
||||
$config = json_encode(array(
|
||||
'base' => array(),
|
||||
'competencies' => array(
|
||||
array('id' => $c2->get_id(), 'points' => 1, 'required' => 0),
|
||||
array('id' => $c3->get_id(), 'points' => 1, 'required' => 0),
|
||||
)
|
||||
));
|
||||
$this->assertFalse($rule->validate_config($config));
|
||||
|
||||
// Invalid required points.
|
||||
$config = json_encode(array(
|
||||
'base' => array('points' => 'abc'),
|
||||
'competencies' => array(
|
||||
array('id' => $c2->get_id(), 'points' => 1, 'required' => 0),
|
||||
array('id' => $c3->get_id(), 'points' => 1, 'required' => 0),
|
||||
)
|
||||
));
|
||||
$this->assertFalse($rule->validate_config($config));
|
||||
|
||||
// Less than 1 required points.
|
||||
$config = json_encode(array(
|
||||
'base' => array('points' => 0),
|
||||
'competencies' => array(
|
||||
array('id' => $c2->get_id(), 'points' => 1, 'required' => 0),
|
||||
array('id' => $c3->get_id(), 'points' => 1, 'required' => 0),
|
||||
)
|
||||
));
|
||||
$this->assertFalse($rule->validate_config($config));
|
||||
|
||||
// Not enough required points.
|
||||
$config = json_encode(array(
|
||||
'base' => array('points' => 3),
|
||||
'competencies' => array(
|
||||
array('id' => $c2->get_id(), 'points' => 1, 'required' => 0),
|
||||
array('id' => $c3->get_id(), 'points' => 1, 'required' => 0),
|
||||
)
|
||||
));
|
||||
$this->assertFalse($rule->validate_config($config));
|
||||
|
||||
// Duplicate competency.
|
||||
$config = json_encode(array(
|
||||
'base' => array('points' => 1),
|
||||
'competencies' => array(
|
||||
array('id' => $c2->get_id(), 'points' => 1, 'required' => 0),
|
||||
array('id' => $c2->get_id(), 'points' => 1, 'required' => 0),
|
||||
)
|
||||
));
|
||||
$this->assertFalse($rule->validate_config($config));
|
||||
|
||||
// Competency includes itself.
|
||||
$config = json_encode(array(
|
||||
'base' => array('points' => 1),
|
||||
'competencies' => array(
|
||||
array('id' => $c1->get_id(), 'points' => 1, 'required' => 0),
|
||||
array('id' => $c2->get_id(), 'points' => 1, 'required' => 0),
|
||||
)
|
||||
));
|
||||
$this->assertFalse($rule->validate_config($config));
|
||||
|
||||
// Cannot use negative points.
|
||||
$config = json_encode(array(
|
||||
'base' => array('points' => 1),
|
||||
'competencies' => array(
|
||||
array('id' => $c2->get_id(), 'points' => -1, 'required' => 0),
|
||||
array('id' => $c3->get_id(), 'points' => 1, 'required' => 0),
|
||||
)
|
||||
));
|
||||
$this->assertFalse($rule->validate_config($config));
|
||||
|
||||
// Not competencies set.
|
||||
$config = json_encode(array(
|
||||
'base' => array('points' => 1),
|
||||
'competencies' => array(
|
||||
)
|
||||
));
|
||||
$this->assertFalse($rule->validate_config($config));
|
||||
|
||||
// There is a competency that is not a child.
|
||||
$config = json_encode(array(
|
||||
'base' => array('points' => 1),
|
||||
'competencies' => array(
|
||||
array('id' => $c1->get_id(), 'points' => 1, 'required' => 0),
|
||||
array('id' => $c2->get_id(), 'points' => 1, 'required' => 0),
|
||||
array('id' => $c3->get_id(), 'points' => 1, 'required' => 0),
|
||||
)
|
||||
));
|
||||
$this->assertFalse($rule->validate_config($config));
|
||||
|
||||
// There is a competency from another framework in there.
|
||||
$config = json_encode(array(
|
||||
'base' => array('points' => 1),
|
||||
'competencies' => array(
|
||||
array('id' => $cx->get_id(), 'points' => 1, 'required' => 0),
|
||||
array('id' => $c3->get_id(), 'points' => 1, 'required' => 0),
|
||||
)
|
||||
));
|
||||
$this->assertFalse($rule->validate_config($config));
|
||||
|
||||
// A normal config.
|
||||
$config = json_encode(array(
|
||||
'base' => array('points' => 4),
|
||||
'competencies' => array(
|
||||
array('id' => $c2->get_id(), 'points' => 3, 'required' => 0),
|
||||
array('id' => $c3->get_id(), 'points' => 2, 'required' => 1),
|
||||
)
|
||||
));
|
||||
$this->assertTrue($rule->validate_config($config));
|
||||
}
|
||||
|
||||
public function test_rule_points_matching() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$this->setAdminUser();
|
||||
$lpg = $this->getDataGenerator()->get_plugin_generator('tool_lp');
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
|
||||
// Set up the framework and competencies.
|
||||
$framework = $lpg->create_framework();
|
||||
$c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id()));
|
||||
$c1->set_ruletype('tool_lp\competency_rule_points');
|
||||
$comprule = new competency_rule_points($c1);
|
||||
$c11 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(), 'parentid' => $c1->get_id()));
|
||||
$c12 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(), 'parentid' => $c1->get_id()));
|
||||
$c13 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(), 'parentid' => $c1->get_id()));
|
||||
$c14 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id(), 'parentid' => $c1->get_id()));
|
||||
|
||||
// Create some user competency records.
|
||||
$uc1 = $lpg->create_user_competency(array('competencyid' => $c1->get_id(), 'userid' => $u1->id));
|
||||
$uc11 = $lpg->create_user_competency(array('competencyid' => $c11->get_id(), 'userid' => $u1->id, 'proficiency' => 1));
|
||||
$uc12 = $lpg->create_user_competency(array('competencyid' => $c12->get_id(), 'userid' => $u1->id, 'proficiency' => 1));
|
||||
$uc13 = $lpg->create_user_competency(array('competencyid' => $c13->get_id(), 'userid' => $u1->id));
|
||||
|
||||
// Enough points.
|
||||
$rule = array(
|
||||
'base' => array('points' => 8),
|
||||
'competencies' => array(
|
||||
array(
|
||||
'id' => $c11->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 0
|
||||
),
|
||||
array(
|
||||
'id' => $c12->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 0
|
||||
),
|
||||
)
|
||||
);
|
||||
$c1->set_ruleconfig(json_encode($rule));
|
||||
$c1->update();
|
||||
$this->assertTrue($comprule->matches($uc1));
|
||||
|
||||
// Not enough points.
|
||||
$rule = array(
|
||||
'base' => array('points' => 8),
|
||||
'competencies' => array(
|
||||
array(
|
||||
'id' => $c11->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 0
|
||||
),
|
||||
array(
|
||||
'id' => $c13->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 0
|
||||
),
|
||||
)
|
||||
);
|
||||
$c1->set_ruleconfig(json_encode($rule));
|
||||
$c1->update();
|
||||
$this->assertFalse($comprule->matches($uc1));
|
||||
|
||||
// One required that is not met but points were OK.
|
||||
$rule = array(
|
||||
'base' => array('points' => 8),
|
||||
'competencies' => array(
|
||||
array(
|
||||
'id' => $c11->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 0
|
||||
),
|
||||
array(
|
||||
'id' => $c12->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 0
|
||||
),
|
||||
array(
|
||||
'id' => $c13->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 1
|
||||
),
|
||||
)
|
||||
);
|
||||
$c1->set_ruleconfig(json_encode($rule));
|
||||
$c1->update();
|
||||
$this->assertFalse($comprule->matches($uc1));
|
||||
|
||||
// One required, one not, should match.
|
||||
$rule = array(
|
||||
'base' => array('points' => 8),
|
||||
'competencies' => array(
|
||||
array(
|
||||
'id' => $c11->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 0
|
||||
),
|
||||
array(
|
||||
'id' => $c12->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 1
|
||||
),
|
||||
)
|
||||
);
|
||||
$c1->set_ruleconfig(json_encode($rule));
|
||||
$c1->update();
|
||||
$this->assertTrue($comprule->matches($uc1));
|
||||
|
||||
// All required and should match.
|
||||
$rule = array(
|
||||
'base' => array('points' => 8),
|
||||
'competencies' => array(
|
||||
array(
|
||||
'id' => $c11->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 1
|
||||
),
|
||||
array(
|
||||
'id' => $c12->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 1
|
||||
),
|
||||
)
|
||||
);
|
||||
$c1->set_ruleconfig(json_encode($rule));
|
||||
$c1->update();
|
||||
$this->assertTrue($comprule->matches($uc1));
|
||||
|
||||
// All required, but one doesn't have a user record.
|
||||
$rule = array(
|
||||
'base' => array('points' => 4),
|
||||
'competencies' => array(
|
||||
array(
|
||||
'id' => $c12->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 1
|
||||
),
|
||||
array(
|
||||
'id' => $c14->get_id(),
|
||||
'points' => 4,
|
||||
'required' => 1
|
||||
),
|
||||
)
|
||||
);
|
||||
$c1->set_ruleconfig(json_encode($rule));
|
||||
$c1->update();
|
||||
$this->assertFalse($comprule->matches($uc1));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -92,6 +92,20 @@ class tool_lp_persistent_testcase extends advanced_testcase {
|
||||
'usermodified' => array(
|
||||
'default' => 0,
|
||||
'type' => PARAM_INT
|
||||
),
|
||||
'ruletype' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'default' => null,
|
||||
'null' => NULL_ALLOWED,
|
||||
),
|
||||
'ruleconfig' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'default' => null,
|
||||
'null' => NULL_ALLOWED,
|
||||
),
|
||||
'ruleoutcome' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'default' => 0
|
||||
)
|
||||
);
|
||||
$this->assertEquals($expected, tool_lp_testable_persistent::properties_definition());
|
||||
@@ -113,6 +127,9 @@ class tool_lp_persistent_testcase extends advanced_testcase {
|
||||
'timecreated' => 0,
|
||||
'timemodified' => 0,
|
||||
'usermodified' => 0,
|
||||
'ruletype' => null,
|
||||
'ruleconfig' => null,
|
||||
'ruleoutcome' => 0,
|
||||
);
|
||||
$this->assertEquals($expected, $p->to_record());
|
||||
}
|
||||
@@ -133,6 +150,9 @@ class tool_lp_persistent_testcase extends advanced_testcase {
|
||||
'timecreated' => 2,
|
||||
'timemodified' => 3,
|
||||
'usermodified' => 4,
|
||||
'ruletype' => null,
|
||||
'ruleconfig' => null,
|
||||
'ruleoutcome' => 0,
|
||||
);
|
||||
$p->from_record($data);
|
||||
$this->assertEquals($data, $p->to_record());
|
||||
@@ -408,6 +428,20 @@ class tool_lp_testable_persistent extends \tool_lp\persistent {
|
||||
'default' => 0,
|
||||
'null' => NULL_ALLOWED
|
||||
),
|
||||
'ruletype' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'default' => null,
|
||||
'null' => NULL_ALLOWED,
|
||||
),
|
||||
'ruleconfig' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'default' => null,
|
||||
'null' => NULL_ALLOWED,
|
||||
),
|
||||
'ruleoutcome' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'default' => 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
$plugin->version = 2015052427; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2015111004; // 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).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user