MDL-50253 tool_lp: Implement a new competency picker

This commit is contained in:
Frederic Massart 2015-10-09 16:12:46 +08:00
parent c70519692a
commit 72c9be0d8f
13 changed files with 512 additions and 366 deletions

View file

@ -1 +1 @@
define(["jquery","core/notification","core/ajax","core/templates","core/str","tool_lp/competencyselector","tool_lp/dragdrop-reorder"],function(a,b,c,d,e,f,g){var h=function(c,e,g){this.itemid=c,this.itemtype=e,this.pageContextId=g;var h=this,i=null,j=null,k=null;"course"===e?(i=[{methodname:"tool_lp_add_competency_to_course",args:{courseid:this.itemid}},{methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:this.itemid}}],j="tool_lp/course_competencies_page",k="coursecompetenciespage"):"template"===e&&(i=[{methodname:"tool_lp_add_competency_to_template",args:{templateid:this.itemid}},{methodname:"tool_lp_data_for_template_competencies_page",args:{templateid:this.itemid}}],j="tool_lp/template_competencies_page",k="templatecompetenciespage");var l=f.init();l.done(function(c){return 0===c.length?void d.render("tool_lp/no_frameworks_warning",{}).done(function(b){a('[data-region="actions"]').append(b),a('[data-region="actions"] button').hide()}).fail(b.exception):(a('[data-region="actions"] button').show(),h.registerEvents(),h.registerDragDrop(),void f.setAddCompetencyRequests(i,j,k))}).fail(b.exception)};return h.prototype.registerDragDrop=function(){var a=this;e.get_string("movecompetency","tool_lp").done(function(b){g.dragdrop("movecompetency",b,{identifier:"movecompetency",component:"tool_lp"},{identifier:"movecompetencyafter",component:"tool_lp"},"drag-samenode","drag-parentnode","drag-handlecontainer",function(b,c){a.handleDrop.call(a,b,c)})}).fail(b.exception)},h.prototype.handleDrop=function(d,e){var f=a(d).data("id"),g=a(e).data("id"),h=this,i=[];if("course"==h.itemtype)i=c.call([{methodname:"tool_lp_reorder_course_competency",args:{courseid:h.itemid,competencyidfrom:f,competencyidto:g}}]);else{if("template"!=h.itemtype)return null;i=c.call([{methodname:"tool_lp_reorder_template_competency",args:{templateid:h.itemid,competencyidfrom:f,competencyidto:g}}])}i[0].fail(b.exception)},h.prototype.registerEvents=function(){var e=this;a('[data-region="actions"] button').click(function(a){return f.openCompetencySelector()}),a('[data-action="delete-competency-link"]').click(function(f){var g=[],h="",i="";f.preventDefault();var j=a(f.target).closest("[data-id]").data("id");"course"==e.itemtype?(g=c.call([{methodname:"tool_lp_remove_competency_from_course",args:{courseid:e.itemid,competencyid:j}},{methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:e.itemid}}]),h="tool_lp/course_competencies_page",i="coursecompetenciespage"):"template"==e.itemtype&&(g=c.call([{methodname:"tool_lp_remove_competency_from_template",args:{templateid:e.itemid,competencyid:j}},{methodname:"tool_lp_data_for_template_competencies_page",args:{templateid:e.itemid,pagecontext:{contextid:e.pageContextId}}}]),h="tool_lp/template_competencies_page",i="templatecompetenciespage"),g[1].done(function(c){d.render(h,c).done(function(b,c){a('[data-region="'+i+'"]').replaceWith(b),d.runTemplateJS(c)}).fail(b.exception)}).fail(b.exception)})},h});
define(["jquery","core/notification","core/ajax","core/templates","core/str","tool_lp/competencypicker","tool_lp/dragdrop-reorder"],function(a,b,c,d,e,f,g){var h=function(b,c,d){this.itemid=b,this.itemtype=c,this.pageContextId=d,this.pickerInstance=null,a('[data-region="actions"] button').show(),this.registerEvents(),this.registerDragDrop()};return h.prototype.registerDragDrop=function(){var a=this;e.get_string("movecompetency","tool_lp").done(function(b){g.dragdrop("movecompetency",b,{identifier:"movecompetency",component:"tool_lp"},{identifier:"movecompetencyafter",component:"tool_lp"},"drag-samenode","drag-parentnode","drag-handlecontainer",function(b,c){a.handleDrop.call(a,b,c)})}).fail(b.exception)},h.prototype.handleDrop=function(d,e){var f=a(d).data("id"),g=a(e).data("id"),h=this,i=[];if("course"==h.itemtype)i=c.call([{methodname:"tool_lp_reorder_course_competency",args:{courseid:h.itemid,competencyidfrom:f,competencyidto:g}}]);else{if("template"!=h.itemtype)return null;i=c.call([{methodname:"tool_lp_reorder_template_competency",args:{templateid:h.itemid,competencyidfrom:f,competencyidto:g}}])}i[0].fail(b.exception)},h.prototype.pickCompetency=function(){var e,g,h,i=this;i.pickerInstance||(i.pickerInstance=new f(i.pageContextId,void 0,"course"===i.itemtype?"parents":void 0),i.pickerInstance.on("save",function(f,j){var k=j.competencyId;"course"===i.itemtype?(e=[{methodname:"tool_lp_add_competency_to_course",args:{courseid:i.itemid,competencyid:k}},{methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:i.itemid}}],g="tool_lp/course_competencies_page",h="coursecompetenciespage"):"template"===i.itemtype&&(e=[{methodname:"tool_lp_add_competency_to_template",args:{templateid:i.itemid,competencyid:k}},{methodname:"tool_lp_data_for_template_competencies_page",args:{templateid:i.itemid,pagecontext:{contextid:i.pageContextId}}}],g="tool_lp/template_competencies_page",h="templatecompetenciespage"),c.call(e)[1].then(function(b){return d.render(g,b).done(function(b,c){a('[data-region="'+h+'"]').replaceWith(b),d.runTemplateJS(c)})},b.exception)})),i.pickerInstance.display()},h.prototype.registerEvents=function(){var e=this;a('[data-region="actions"] button').click(function(a){a.preventDefault(),e.pickCompetency()}),a('[data-action="delete-competency-link"]').click(function(f){var g=[],h="",i="";f.preventDefault();var j=a(f.target).closest("[data-id]").data("id");"course"==e.itemtype?(g=c.call([{methodname:"tool_lp_remove_competency_from_course",args:{courseid:e.itemid,competencyid:j}},{methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:e.itemid}}]),h="tool_lp/course_competencies_page",i="coursecompetenciespage"):"template"==e.itemtype&&(g=c.call([{methodname:"tool_lp_remove_competency_from_template",args:{templateid:e.itemid,competencyid:j}},{methodname:"tool_lp_data_for_template_competencies_page",args:{templateid:e.itemid,pagecontext:{contextid:e.pageContextId}}}]),h="tool_lp/template_competencies_page",i="templatecompetenciespage"),g[1].done(function(c){d.render(h,c).done(function(b,c){a('[data-region="'+i+'"]').replaceWith(b),d.runTemplateJS(c)}).fail(b.exception)}).fail(b.exception)})},h});

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
define(["jquery","core/notification","core/ajax","core/templates","tool_lp/dialogue","core/str","tool_lp/tree"],function(a,b,c,d,e,f,g){var h,i=function(b,c,d){h=this,h._eventNode=a("<div></div>"),h._frameworks=[],h._reset(),h._pageContextId=b,h._pageContextIncludes=d||"children",c&&(h._frameworkId=c,h._singleFramework=!0)};return i.prototype._competencies=null,i.prototype._disallowedCompetencyIDs=null,i.prototype._eventNode=null,i.prototype._frameworks=null,i.prototype._frameworkId=null,i.prototype._pageContextId=null,i.prototype._pageContextIncludes=null,i.prototype._popup=null,i.prototype._searchText="",i.prototype._selectedCompetency=null,i.prototype._singleFramework=!1,i.prototype._afterRender=function(){new g(h._find("[data-enhance=linktree]"),function(b){var c=b.data("id"),d=!0;a.each(h._disallowedCompetencyIDs,function(a,b){return b==c?(d=!1,!1):void 0}),h._selectedCompetency=null,d&&(h._selectedCompetency=c),h._selectedCompetency?h._find('[data-region="competencylinktree"] [data-action="add"]').removeAttr("disabled"):h._find('[data-region="competencylinktree"] [data-action="add"]').attr("disabled","disabled")}),h._singleFramework||h._find('[data-action="chooseframework"]').change(function(b){h._frameworkId=a(b.target).val(),h._loadCompetencies().then(h._refresh)}),h._find('[data-region="filtercompetencies"] button').click(function(b){return b.preventDefault(),a(b.target).attr("disabled","disabled"),h._searchText=h._find('[data-region="filtercompetencies"] input').val()||"",h._refresh().always(function(){a(b.target).removeAttr("disabled")})}),h._find('[data-region="competencylinktree"] [data-action="cancel"]').click(function(a){a.preventDefault(),h.close()}),h._find('[data-region="competencylinktree"] [data-action="add"]').click(function(a){a.preventDefault(),h._selectedCompetency&&(h._trigger("save",{competencyId:h._selectedCompetency}),h.close())})},i.prototype.close=function(){h._popup.close(),h._reset()},i.prototype.display=function(){return h._render().done(function(a){return f.get_string("competencypicker","tool_lp").done(function(b){h._popup=new e(b,a,h._afterRender.bind(h))})}).fail(b.exception)},i.prototype._fetchCompetencies=function(a,d){return c.call([{methodname:"tool_lp_search_competencies",args:{searchtext:d,competencyframeworkid:a}}])[0].done(function(a){function b(a,c){for(var d=0;d<c.length;d++)c[d].parentid==a.id&&(a.haschildren=!0,c[d].children=[],c[d].haschildren=!1,a.children[a.children.length]=c[d],b(c[d],c))}var c,d,e=[];for(c=0;c<a.length;c++)d=a[c],"0"==d.parentid&&(d.children=[],d.haschildren=0,e[e.length]=d,b(d,a));h._competencies=e}).fail(b.exception)},i.prototype._find=function(b){return a(h._popup.getContent()).find(b)},i.prototype._getFramework=function(b){var c;return a.each(h._frameworks,function(a,d){return d.id==b?(c=d,!1):void 0}),c},i.prototype._loadCompetencies=function(){return h._fetchCompetencies(h._frameworkId,h._searchText)},i.prototype._loadFrameworks=function(){var d;return h._frameworks.length>0?a.when():(d=h._singleFramework?c.call([{methodname:"tool_lp_read_competency_framework",args:{id:this._frameworkId}}])[0].then(function(a){return[a]}):c.call([{methodname:"tool_lp_list_competency_frameworks",args:{sort:"shortname",context:{contextid:h._pageContextId},includes:h._pageContextIncludes}}])[0],d.done(function(a){h._frameworks=a}).fail(b.exception))},i.prototype.on=function(a,b){h._eventNode.on(a,b)},i.prototype._preRender=function(){return h._loadFrameworks().then(function(){return!h._frameworkId&&h._frameworks.length>0&&(h._frameworkId=h._frameworks[0].id),h._frameworkId?h._loadCompetencies():(h._frameworks=[],a.when())})},i.prototype._refresh=function(){return h._render().then(function(a){h._find('[data-region="competencylinktree"]').replaceWith(a),h._afterRender()})},i.prototype._render=function(){return h._preRender().then(function(){h._singleFramework||a.each(h._frameworks,function(a,b){b.id==h._frameworkId?b.selected=!0:b.selected=!1});var b={competencies:h._competencies,framework:h._getFramework(h._frameworkId),frameworks:h._frameworks,search:h._searchText,singleFramework:h._singleFramework};return d.render("tool_lp/competency_picker",b)})},i.prototype._reset=function(){h._competencies=[],h._disallowedCompetencyIDs=[],h._popup=null,h._searchText="",h._selectedCompetency=null},i.prototype.setDisallowedCompetencyIDs=function(a){h._disallowedCompetencyIDs=a},i.prototype._trigger=function(a,b){h._eventNode.trigger(a,[b])},i});

View file

@ -1 +0,0 @@
define(["jquery","core/notification","core/ajax","core/templates","tool_lp/dialogue","core/str","tool_lp/tree"],function(a,b,c,d,e,f,g){return{frameworks:null,popup:null,selectedCompetency:0,requests:[],pagerender:null,pageregion:null,addCompetencyCallback:null,init:function(){var a=c.call([{methodname:"tool_lp_list_competency_frameworks",args:{filters:{},sort:"sortorder"}}]);return a[0].done(function(a){this.frameworks=a}.bind(this)).fail(b.exception),a[0]},setAddCompetencyRequests:function(a,b,c,d){this.requests=a,this.pagerender=b,this.pageregion=c,this.addCompetencyCallback=d},applyFilter:function(c){c.preventDefault();var e=a('[data-region="filtercompetencies"] input'),f=e.val(),g=a('[data-action="chooseframework"]'),h=g.val();this.searchCompetencies().done(function(c){var e=0,g=this.frameworks[0];for(e=0;e<this.frameworks.length;e++)this.frameworks[e].id==h?(g=this.frameworks[e],g.selected=!0):this.frameworks[e].selected=!1;g.selected=!0;var i={framework:g,frameworks:this.frameworks,competencies:c,search:f};d.render("tool_lp/link_course_competencies",i).done(function(b){a('[data-region="competencylinktree"]').replaceWith(b),this.initLinkCourseCompetencies()}.bind(this)).fail(b.exception)}.bind(this)).fail(b.exception)},initLinkCourseCompetencies:function(){new g("[data-enhance=linktree]",function(a){this.selectedCompetency=a.data("id")}.bind(this)),a('[data-action="chooseframework"]').change(function(a){return this.applyFilter(a)}.bind(this)),a('[data-region="filtercompetencies"] button').click(function(b){return a(b.target).attr("disabled","disabled"),this.applyFilter(b)}.bind(this)),a('[data-region="competencylinktree"] [data-action="cancel"]').click(function(b){a(b.target).attr("disabled","disabled"),b.preventDefault(),this.popup.close()}.bind(this)),a('[data-region="competencylinktree"] [data-action="add"]').click(function(e){e.preventDefault(),this.selectedCompetency&&(a(e.target).attr("disabled","disabled"),this.requests[0].args.competencyid=this.selectedCompetency,requests=c.call(this.requests),requests[1].done(function(c){d.render(this.pagerender,c).done(function(b,c){this.popup.close(),a('[data-region="'+this.pageregion+'"]').replaceWith(b),d.runTemplateJS(c),"undefined"!=typeof this.addCompetencyCallback&&this.addCompetencyCallback()}.bind(this)).fail(b.exception)}.bind(this)).fail(b.exception))}.bind(this))},addCompetencyChildren:function(a,b){var c;for(c=0;c<b.length;c++)b[c].parentid==a.id&&(a.haschildren=!0,b[c].children=[],b[c].haschildren=!1,a.children[a.children.length]=b[c],this.addCompetencyChildren(b[c],b))},searchCompetencies:function(){var b=a.Deferred(),d=a('[data-region="filtercompetencies"] input'),e="";d.length&&(e=d.val());var f=a('[data-action="chooseframework"]'),g=this.frameworks[0].id;f.length&&(g=f.val());var h=c.call([{methodname:"tool_lp_search_competencies",args:{searchtext:e,competencyframeworkid:g}}]);return h[0].done(function(a){var c,d=[];for(c=0;c<a.length;c++){var e=a[c];0===e.parentid&&(e.children=[],e.haschildren=0,d[d.length]=e,this.addCompetencyChildren(e,a))}b.resolve(d)}.bind(this)).fail(function(a){b.reject(a)}),b.promise()},openCompetencySelector:function(){this.searchCompetencies().done(function(a){var c=this.frameworks[0];c.selected=!0;var g={framework:c,frameworks:this.frameworks,competencies:a,search:""};d.render("tool_lp/link_course_competencies",g).done(function(a){f.get_string("linkcompetencies","tool_lp").done(function(b){this.popup=new e(b,a,function(){this.initLinkCourseCompetencies()}.bind(this))}.bind(this)).fail(b.exception)}.bind(this)).fail(b.exception)}.bind(this)).fail(b.exception)}}});

View file

@ -1 +1 @@
define(["core/yui"],function(a){var b=function(b,c,d){this.yuiDialogue=null;var e=this;a.use("moodle-core-notification",function(){e.yuiDialogue=new M.core.dialogue({headerContent:b,bodyContent:c,draggable:!0,visible:!1,center:!0,modal:!0}),e.yuiDialogue.after("visibleChange",function(a){a.newVal&&d(e)}),e.yuiDialogue.show()})};return b.prototype.close=function(){this.yuiDialogue.hide(),this.yuiDialogue.destroy()},b.prototype.getContent=function(){return this.yuiDialogue.bodyNode.getDOMNode()},b});
define(["core/yui"],function(a){var b=function(b,c,d){this.yuiDialogue=null;var e=this;a.use("moodle-core-notification","timers",function(){e.yuiDialogue=new M.core.dialogue({headerContent:b,bodyContent:c,draggable:!0,visible:!1,center:!0,modal:!0}),e.yuiDialogue.after("visibleChange",function(b){b.newVal&&a.soon(function(){d(e)})}),e.yuiDialogue.show()})};return b.prototype.close=function(){this.yuiDialogue.hide(),this.yuiDialogue.destroy()},b.prototype.getContent=function(){return this.yuiDialogue.bodyNode.getDOMNode()},b});

View file

@ -26,9 +26,9 @@ define(['jquery',
'core/ajax',
'core/templates',
'core/str',
'tool_lp/competencyselector',
'tool_lp/competencypicker',
'tool_lp/dragdrop-reorder'],
function($, notification, ajax, templates, str, competencyselector, dragdrop) {
function($, notification, ajax, templates, str, Picker, dragdrop) {
/**
* Constructor
@ -40,50 +40,11 @@ define(['jquery',
this.itemid = itemid;
this.itemtype = itemtype;
this.pageContextId = pagectxid;
this.pickerInstance = null;
var localthis = this;
var requests = null;
var pagerender = null;
var pageregion = null;
if (itemtype === "course") {
requests = [
{ methodname: 'tool_lp_add_competency_to_course',
args: { courseid: this.itemid } },
{ methodname: 'tool_lp_data_for_course_competencies_page',
args: { courseid: this.itemid } }
];
pagerender = 'tool_lp/course_competencies_page';
pageregion = 'coursecompetenciespage';
} else if (itemtype === "template") {
requests = [
{ methodname: 'tool_lp_add_competency_to_template',
args: { templateid: this.itemid } },
{ methodname: 'tool_lp_data_for_template_competencies_page',
args: { templateid: this.itemid } }
];
pagerender = 'tool_lp/template_competencies_page';
pageregion = 'templatecompetenciespage';
}
var promise = competencyselector.init(this.pageContextId);
promise.done(function(frameworks) {
if (frameworks.length === 0) {
templates.render('tool_lp/no_frameworks_warning', {})
.done(function(html) {
$('[data-region="actions"]').append(html);
$('[data-region="actions"] button').hide();
}).fail(notification.exception);
return;
}
$('[data-region="actions"] button').show();
localthis.registerEvents();
localthis.registerDragDrop();
// And we finally attach the callbacks to execute once the user selected a competency to add.
competencyselector.setAddCompetencyRequests(requests, pagerender, pageregion);
}).fail(notification.exception);
this.registerEvents();
this.registerDragDrop();
};
/**
@ -144,6 +105,55 @@ define(['jquery',
requests[0].fail(notification.exception);
};
/**
* Pick a competency
*
* @method pickCompetency
*/
competencies.prototype.pickCompetency = function() {
var self = this;
var requests;
var pagerender;
var pageregion;
if (!self.pickerInstance) {
self.pickerInstance = new Picker(self.pageContextId, undefined, self.itemtype === 'course' ? 'parents' : undefined);
self.pickerInstance.on('save', function(e, data) {
var compId = data.competencyId;
if (self.itemtype === "course") {
requests = [
{ methodname: 'tool_lp_add_competency_to_course',
args: { courseid: self.itemid, competencyid: compId } },
{ methodname: 'tool_lp_data_for_course_competencies_page',
args: { courseid: self.itemid } }
];
pagerender = 'tool_lp/course_competencies_page';
pageregion = 'coursecompetenciespage';
} else if (self.itemtype === "template") {
requests = [
{ methodname: 'tool_lp_add_competency_to_template',
args: { templateid: self.itemid, competencyid: compId }},
{ methodname: 'tool_lp_data_for_template_competencies_page',
args: { templateid: self.itemid, pagecontext: { contextid: self.pageContextId }}}
];
pagerender = 'tool_lp/template_competencies_page';
pageregion = 'templatecompetenciespage';
}
ajax.call(requests)[1].then(function(context) {
return templates.render(pagerender, context).done(function(html, js) {
$('[data-region="' + pageregion + '"]').replaceWith(html);
templates.runTemplateJS(js);
});
}, notification.exception);
});
}
self.pickerInstance.display();
};
/**
* Register the javascript event handlers for this page.
*
@ -152,7 +162,8 @@ define(['jquery',
competencies.prototype.registerEvents = function() {
var localthis = this;
$('[data-region="actions"] button').click(function(e) {
return competencyselector.openCompetencySelector();
e.preventDefault();
localthis.pickCompetency();
});
$('[data-action="delete-competency-link"]').click(function(e) {
var requests = [],

View file

@ -31,8 +31,8 @@ define(['jquery',
'tool_lp/tree',
'tool_lp/dialogue',
'tool_lp/menubar',
'tool_lp/competencyselector'],
function($, url, templates, notification, str, ajax, dragdrop, Ariatree, Dialogue, menubar, competencyselector) {
'tool_lp/competencypicker'],
function($, url, templates, notification, str, ajax, dragdrop, Ariatree, Dialogue, menubar, Picker) {
// Private variables and functions.
/** @var {Object} treeModel - This is an object representing the nodes in the tree. */
@ -43,6 +43,10 @@ define(['jquery',
var moveTarget = null;
/** @var {Number} pageContextId The page context ID. */
var pageContextId;
/** @type {Object} Picker instance. */
var pickerInstance;
/** @type {Object} The competency we're picking a relation to. */
var relatedTarget;
/**
* Respond to choosing the "Add" menu item for the selected node in the tree.
@ -158,7 +162,7 @@ define(['jquery',
var i, competenciestree = [];
for (i = 0; i < competencies.length; i++) {
var onecompetency = competencies[i];
if (onecompetency.parentid == 0) {
if (onecompetency.parentid == "0") {
onecompetency.children = [];
onecompetency.haschildren = 0;
competenciestree[competenciestree.length] = onecompetency;
@ -312,22 +316,32 @@ define(['jquery',
* @method relateCompetenciesHandler
*/
var relateCompetenciesHandler = function() {
relatedTarget = $('[data-region="competencyactions"]').data('competency');
var competency = $('[data-region="competencyactions"]').data('competency');
if (!pickerInstance) {
pickerInstance = new Picker(pageContextId, relatedTarget.competencyframeworkid);
pickerInstance.on('save', function(e, data) {
var compId = data.competencyId;
// Initialise the competency selector.
var requests = [
var promises = ajax.call([
{ methodname: 'tool_lp_add_related_competency',
args: { relatedcompetencyid: competency.id } },
args: { competencyid: compId, relatedcompetencyid: relatedTarget.id }},
{ methodname: 'tool_lp_data_for_related_competencies_section',
args: { competencyid: competency.id } }
];
var pagerender = 'tool_lp/related_competencies';
var pageregion = 'relatedcompetencies';
args: { competencyid: relatedTarget.id }}
]);
// Attach a callback to execute after the related competencies list is updated.
competencyselector.setAddCompetencyRequests(requests, pagerender, pageregion, updatedRelatedCompetencies);
competencyselector.openCompetencySelector();
promises[1].then(function(context) {
return templates.render('tool_lp/related_competencies', context).done(function(html, js) {
$('[data-region="relatedcompetencies"]').replaceWith(html);
templates.runTemplateJS(js);
updatedRelatedCompetencies();
});
}, notification.exception);
});
}
pickerInstance.setDisallowedCompetencyIDs([relatedTarget.id]);
pickerInstance.display();
};
/**
@ -480,7 +494,7 @@ define(['jquery',
treeModel = model;
pageContextId = pagectxid;
$('[data-region="competencyactions"]').on('click', addHandler);
$('[data-region="competencyactions"] [data-action="add"]').on('click', addHandler);
menubar.enhance('.competencyactionsmenu', {
'[data-action="edit"]': editHandler,
@ -489,7 +503,7 @@ define(['jquery',
'[data-action="moveup"]': moveUpHandler,
'[data-action="movedown"]': moveDownHandler,
'[data-action="linkedcourses"]': seeCoursesHandler,
'[data-action="relatedcompetencies"]': relateCompetenciesHandler
'[data-action="relatedcompetencies"]': relateCompetenciesHandler.bind(this)
});
$('[data-region="competencyactionsmenu"]').hide();
@ -500,8 +514,6 @@ define(['jquery',
$('[data-region="managecompetencies"] li').on('dragenter', dragEnter);
$('[data-region="managecompetencies"] li').on('dragleave', dragLeave);
$('[data-region="managecompetencies"] li').on('drop', dropOver);
competencyselector.init(pageContextId);
},
/**

View file

@ -0,0 +1,409 @@
// self 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 picker.
*
* @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/ajax',
'core/templates',
'tool_lp/dialogue',
'core/str',
'tool_lp/tree'],
function($, Notification, Ajax, Templates, Dialogue, Str, Tree) {
var self;
/**
* Competency picker class.
* @param {Number} pageContextId The page context ID.
* @param {Number|false} singleFramework The ID of the framework when limited to one.
*/
var Picker = function(pageContextId, singleFramework, pageContextIncludes) {
self = this;
self._eventNode = $('<div></div>');
self._frameworks = [];
self._reset();
self._pageContextId = pageContextId;
self._pageContextIncludes = pageContextIncludes || 'children';
if (singleFramework) {
self._frameworkId = singleFramework;
self._singleFramework = true;
}
};
/** @type {Array} The competencies fetched. */
Picker.prototype._competencies = null;
/** @type {Array} The competencies that cannot be picked. */
Picker.prototype._disallowedCompetencyIDs = null;
/** @type {Node} The node we attach the events to. */
Picker.prototype._eventNode = null;
/** @type {Array} The list of frameworks fetched. */
Picker.prototype._frameworks = null;
/** @type {Number} The current framework ID. */
Picker.prototype._frameworkId = null;
/** @type {Number} The page context ID. */
Picker.prototype._pageContextId = null;
/** @type {Number} Relevant contexts inclusion. */
Picker.prototype._pageContextIncludes = null;
/** @type {Dialogue} The reference to the dialogue. */
Picker.prototype._popup = null;
/** @type {String} The string we filter the competencies with. */
Picker.prototype._searchText = '';
/** @type {Object} The competency that was selected. */
Picker.prototype._selectedCompetency = null;
/** @type {Boolean} Whether we can browser frameworks or not. */
Picker.prototype._singleFramework = false;
/**
* Hook to executed after the view is rendered.
*
* @method _afterRender
*/
Picker.prototype._afterRender = function() {
// Initialise the tree.
new Tree(self._find('[data-enhance=linktree]'), function(target) {
var compId = target.data('id'),
valid = true;
$.each(self._disallowedCompetencyIDs, function(i, id) {
if (id == compId) {
valid = false;
return false;
}
});
self._selectedCompetency = null;
if (valid) {
self._selectedCompetency = compId;
}
// TODO Implement disabling of nodes in the tree module somehow.
if (!self._selectedCompetency) {
self._find('[data-region="competencylinktree"] [data-action="add"]').attr('disabled', 'disabled');
} else {
self._find('[data-region="competencylinktree"] [data-action="add"]').removeAttr('disabled');
}
});
// Add listener for framework change.
if (!self._singleFramework) {
self._find('[data-action="chooseframework"]').change(function(e) {
self._frameworkId = $(e.target).val();
self._loadCompetencies().then(self._refresh);
});
}
// Add listener for search.
self._find('[data-region="filtercompetencies"] button').click(function(e) {
e.preventDefault();
$(e.target).attr('disabled', 'disabled');
self._searchText = self._find('[data-region="filtercompetencies"] input').val() || '';
return self._refresh().always(function() {
$(e.target).removeAttr('disabled');
});
});
// Add listener for cancel.
self._find('[data-region="competencylinktree"] [data-action="cancel"]').click(function(e) {
e.preventDefault();
self.close();
});
// Add listener for add.
self._find('[data-region="competencylinktree"] [data-action="add"]').click(function(e) {
e.preventDefault();
if (!self._selectedCompetency) {
return;
}
self._trigger('save', { competencyId: self._selectedCompetency });
self.close();
});
};
/**
* Close the dialogue.
*
* @method close
*/
Picker.prototype.close = function() {
self._popup.close();
self._reset();
};
/**
* Opens the picker.
*
* @method display
* @return {Promise}
*/
Picker.prototype.display = function() {
return self._render().done(function(html) {
return Str.get_string('competencypicker', 'tool_lp').done(function(title) {
self._popup = new Dialogue(
title,
html,
self._afterRender.bind(self)
);
});
}).fail(Notification.exception);
};
/**
* Fetch the competencies.
*
* @param {Number} frameworkId The frameworkId.
* @param {String} searchText Limit the competencies to those matching the text.
* @method _fetchCompetencies
* @return {Promise}
*/
Picker.prototype._fetchCompetencies = function(frameworkId, searchText) {
return Ajax.call([
{ methodname: 'tool_lp_search_competencies', args: {
searchtext: searchText,
competencyframeworkid: frameworkId
}}
])[0].done(function(competencies) {
function addCompetencyChildren(parent, competencies) {
for (var i = 0; i < competencies.length; i++) {
if (competencies[i].parentid == parent.id) {
parent.haschildren = true;
competencies[i].children = [];
competencies[i].haschildren = false;
parent.children[parent.children.length] = competencies[i];
addCompetencyChildren(competencies[i], competencies);
}
}
}
// Expand the list of competencies into a tree.
var i, tree = [], comp;
for (i = 0; i < competencies.length; i++) {
comp = competencies[i];
if (comp.parentid == "0") { // Loose check for now, because WS returns a string.
comp.children = [];
comp.haschildren = 0;
tree[tree.length] = comp;
addCompetencyChildren(comp, competencies);
}
}
self._competencies = tree;
}).fail(Notification.exception);
};
/**
* Find a node in the dialogue.
*
* @param {String} selector
* @method _find
*/
Picker.prototype._find = function(selector) {
return $(self._popup.getContent()).find(selector);
};
/**
* Convenience method to get a framework object.
*
* @param {Number} fid The framework ID.
* @method _getFramework
*/
Picker.prototype._getFramework = function(fid) {
var frm;
$.each(self._frameworks, function(i, f) {
if (f.id == fid) {
frm = f;
return false;
}
});
return frm;
};
/**
* Load the competencies.
*
* @method _loadCompetencies
* @return {Promise}
*/
Picker.prototype._loadCompetencies = function() {
return self._fetchCompetencies(self._frameworkId, self._searchText);
};
/**
* Load the frameworks.
*
* @method _loadFrameworks
* @return {Promise}
*/
Picker.prototype._loadFrameworks = function() {
var promise;
// Quit early because we already have the data.
if (self._frameworks.length > 0) {
return $.when();
}
if (self._singleFramework) {
promise = Ajax.call([
{ methodname: 'tool_lp_read_competency_framework', args: {
id: this._frameworkId
}}
])[0].then(function(framework) {
return [framework];
});
} else {
promise = Ajax.call([
{ methodname: 'tool_lp_list_competency_frameworks', args: {
sort: 'shortname',
context: { contextid: self._pageContextId },
includes: self._pageContextIncludes
}}
])[0];
}
return promise.done(function(frameworks) {
self._frameworks = frameworks;
}).fail(Notification.exception);
};
/**
* Register an event listener.
*
* @param {String} type The event type.
* @param {Function} handler The event listener.
* @method on
*/
Picker.prototype.on = function(type, handler) {
self._eventNode.on(type, handler);
};
/**
* Hook to executed before render.
*
* @method _preRender
* @return {Promise}
*/
Picker.prototype._preRender = function() {
return self._loadFrameworks().then(function() {
if (!self._frameworkId && self._frameworks.length > 0) {
self._frameworkId = self._frameworks[0].id;
}
// We could not set a framework ID, that probably means there are no frameworks accessible.
if (!self._frameworkId) {
self._frameworks = [];
return $.when();
}
return self._loadCompetencies();
});
};
/**
* Refresh the view.
*
* @method _refresh
* @return {Promise}
*/
Picker.prototype._refresh = function() {
return self._render().then(function(html) {
self._find('[data-region="competencylinktree"]').replaceWith(html);
self._afterRender();
});
};
/**
* Render the dialogue.
*
* @method _render
* @return {Promise}
*/
Picker.prototype._render = function() {
return self._preRender().then(function() {
if (!self._singleFramework) {
$.each(self._frameworks, function(i, framework) {
if (framework.id == self._frameworkId) {
framework.selected = true;
} else {
framework.selected = false;
}
});
}
var context = {
competencies: self._competencies,
framework: self._getFramework(self._frameworkId),
frameworks: self._frameworks,
search: self._searchText,
singleFramework: self._singleFramework,
};
return Templates.render('tool_lp/competency_picker', context);
});
};
/**
* Reset the dialogue properties.
*
* This does not reset everything, just enough to reset the UI.
*
* @method _reset
*/
Picker.prototype._reset = function() {
self._competencies = [];
self._disallowedCompetencyIDs = [];
self._popup = null;
self._searchText = '';
self._selectedCompetency = null;
};
/**
* Set what competencies cannot be picked.
*
* This needs to be set after reset/close.
*
* @params {Number[]} The IDs.
* @method _setDisallowedCompetencyIDs
*/
Picker.prototype.setDisallowedCompetencyIDs = function(ids) {
self._disallowedCompetencyIDs = ids;
};
/**
* Trigger an event.
*
* @param {String} type The type of event.
* @param {Object} The data to pass to the listeners.
* @method _reset
*/
Picker.prototype._trigger = function(type, data) {
self._eventNode.trigger(type, [data]);
};
return /** @alias module:tool_lp/competencypicker */ Picker;
});

View file

@ -1,293 +0,0 @@
// 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 selector handler.
*
* @module tool_lp/competencyselector
* @package tool_lp
* @copyright 2015 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery',
'core/notification',
'core/ajax',
'core/templates',
'tool_lp/dialogue',
'core/str',
'tool_lp/tree'],
function($, notification, ajax, templates, Dialogue, str, Ariatree) {
return {
// Private variables and functions.
/** @var {Object} frameworks - Site frameworks data */
frameworks : null,
/** @var {Dialogue} popup - Pop up where competencies are displayed. */
popup : null,
/** @var {Number} selectedCompetency - Currently selected competency. */
selectedCompetency : 0,
/** @var {Object[]} requests - Requests specified by the dependant module. */
requests : [],
/** @var {String} pagerender - Template to render, specified by the dependant module. */
pagerender : null,
/** @var {String} pageregion - Region to render in, specified by the dependant module. */
pageregion : null,
/** @var {Callback} addCompetencyCallback - Additional stuff to execute once the region is updated. */
addCompetencyCallback : null,
/** @var {Number} The page context ID. */
pageContextId: null,
/**
* Returns the load competency frameworks promise.
*
* Caller can act depending on whether frameworks were found or not.
*
* @param {Number} pagectxid The page context ID.
* @return {Promise}
* @method init
*/
init : function(pagectxid) {
this.pageContextId = pagectxid;
var loadframeworks = ajax.call([
{ methodname: 'tool_lp_list_competency_frameworks', args: {
sort: 'shortname',
context: { contextid: this.pageContextId }
}}
]);
loadframeworks[0].done(function(frameworks) {
this.frameworks = frameworks;
}.bind(this)).fail(notification.exception);
return loadframeworks[0];
},
/**
* Sets the data required by the module.
* @param {Object[]} requests - Array of objects with the requests to send once a competency is selected.
* @param {String} pagerender - The template to render.
* @param {String} pageregion - The region to render in.
* @param {Callback} addCompetencyCallback - Additional stuff to execute once all done.
* @method setAddCompetencyRequests
*/
setAddCompetencyRequests : function(requests, pagerender, pageregion, addCompetencyCallback) {
this.requests = requests;
this.pagerender = pagerender;
this.pageregion = pageregion;
this.addCompetencyCallback = addCompetencyCallback;
},
/**
* Get the search text from the input field and reload the tree based on the search.
*
* @method applyFilter
* @param {Event} e The event that triggered the button.
*/
applyFilter : function(e) {
e.preventDefault();
var searchInput = $('[data-region="filtercompetencies"] input');
var searchText = searchInput.val();
var framework = $('[data-action="chooseframework"]');
var frameworkid = framework.val();
this.searchCompetencies().done(function (competencies) {
var i = 0;
var framework = this.frameworks[0];
for (i = 0; i < this.frameworks.length; i++) {
if (this.frameworks[i].id == frameworkid) {
framework = this.frameworks[i];
framework.selected = true;
} else {
this.frameworks[i].selected = false;
}
}
framework.selected = true;
var context = {
framework: framework,
frameworks: this.frameworks,
competencies: competencies,
search: searchText
};
templates.render('tool_lp/link_competencies', context).done(function(html) {
$('[data-region="competencylinktree"]').replaceWith(html);
this.initLinkCourseCompetencies();
}.bind(this)).fail(notification.exception);
}.bind(this)).fail(notification.exception);
},
/**
* The link course competencies popup was just opened and we need to initialise it.
*
* @method initLinkCourseCompetencies
*/
initLinkCourseCompetencies : function() {
var requests;
new Ariatree('[data-enhance=linktree]', function(target) {
this.selectedCompetency = target.data('id');
}.bind(this));
$('[data-action="chooseframework"]').change(function(e) {
return this.applyFilter(e);
}.bind(this));
$('[data-region="filtercompetencies"] button').click(function(e) {
$(e.target).attr('disabled', 'disabled');
return this.applyFilter(e);
}.bind(this));
$('[data-region="competencylinktree"] [data-action="cancel"]').click(function(e) {
$(e.target).attr('disabled', 'disabled');
e.preventDefault();
this.popup.close();
}.bind(this));
$('[data-region="competencylinktree"] [data-action="add"]').click(function(e) {
var btn = $(e.target);
e.preventDefault();
if (!this.selectedCompetency) {
return;
}
btn.attr('disabled', 'disabled');
// The required callbacks and rendered templates depends on the page, but we should always
// attach the selectCompetency to the first request, the one adding data to the database.
this.requests[0].args.competencyid = this.selectedCompetency;
requests = ajax.call(this.requests);
requests[1].done(function(context) {
templates.render(this.pagerender, context).done(function(html, js) {
this.popup.close();
$('[data-region="' + this.pageregion + '"]').replaceWith(html);
templates.runTemplateJS(js);
// Extra callback in case we need additional processes.
if (typeof this.addCompetencyCallback !== "undefined") {
this.addCompetencyCallback();
}
}.bind(this)).fail(function(err) {
btn.removeAttr('disabled');
notification.exception(err);
});
}.bind(this)).fail(function(err) {
btn.removeAttr('disabled');
notification.exception(err);
});
}.bind(this));
},
/**
* Turn the flat list of competencies into a tree.
*
* @method addCompetencyChildren
* @param {Object} parent The current parent node
* @param {Object[]} competencies The flat list of all nodes.
*/
addCompetencyChildren : function(parent, competencies) {
var i;
for (i = 0; i < competencies.length; i++) {
if (competencies[i].parentid == parent.id) {
parent.haschildren = true;
competencies[i].children = [];
competencies[i].haschildren = false;
parent.children[parent.children.length] = competencies[i];
this.addCompetencyChildren(competencies[i], competencies);
}
}
},
/**
* Get the search text from the input, and reload the tree.
*
* @method searchCompetencies
* @return {promise} When resolved it will contain the tree of competencies.
*/
searchCompetencies : function() {
var deferred = $.Deferred();
var searchInput = $('[data-region="filtercompetencies"] input');
var searchText = '';
if (searchInput.length) {
searchText = searchInput.val();
}
var framework = $('[data-action="chooseframework"]');
var frameworkid = this.frameworks[0].id;
if (framework.length) {
frameworkid = framework.val();
}
var loadCompetencies = ajax.call([
{ methodname: 'tool_lp_search_competencies', args: {
searchtext: searchText,
competencyframeworkid: frameworkid
}}
]);
loadCompetencies[0].done(function (competencies) {
// Expand the list of competencies into a tree.
var i, competenciestree = [];
for (i = 0; i < competencies.length; i++) {
var onecompetency = competencies[i];
if (onecompetency.parentid == 0) {
onecompetency.children = [];
onecompetency.haschildren = 0;
competenciestree[competenciestree.length] = onecompetency;
this.addCompetencyChildren(onecompetency, competencies);
}
}
deferred.resolve(competenciestree);
}.bind(this)).fail(function (ex) { deferred.reject(ex); });
return deferred.promise();
},
/**
* Open a popup to choose competencies.
*
* @method openCompetencySelector
*/
openCompetencySelector : function() {
this.searchCompetencies().done(function (competencies) {
var framework = this.frameworks[0];
framework.selected = true;
var context = { framework: framework, frameworks: this.frameworks, competencies: competencies, search: '' };
templates.render('tool_lp/link_competencies', context).done(function(html) {
str.get_string('linkcompetencies', 'tool_lp').done(function(title) {
this.popup = new Dialogue(
title,
html, // The link UI.
function() {
this.initLinkCourseCompetencies();
}.bind(this)
);
}.bind(this)).fail(notification.exception);
}.bind(this)).fail(notification.exception);
}.bind(this)).fail(notification.exception);
}
};
});

View file

@ -36,7 +36,7 @@ define(['core/yui'], function(Y) {
this.yuiDialogue = null;
var parent = this;
Y.use('moodle-core-notification', function () {
Y.use('moodle-core-notification', 'timers', function () {
parent.yuiDialogue = new M.core.dialogue({
headerContent: title,
@ -49,7 +49,11 @@ define(['core/yui'], function(Y) {
parent.yuiDialogue.after('visibleChange', function(e) {
if (e.newVal) {
// Delay the callback call to the next tick, otherwise it can happen that it is
// executed before the dialogue constructor returns.
Y.soon(function() {
afterShow(parent);
});
}
});

View file

@ -37,6 +37,7 @@ $string['competencyframeworkcreated'] = 'Competency framework created.';
$string['competencyframeworkname'] = 'Name';
$string['competencyframeworks'] = 'Competency Frameworks';
$string['competencyframeworkupdated'] = 'Competency framework updated.';
$string['competencypicker'] = 'Competency picker';
$string['competencyrelatedcompetencies'] = '{$a} related competencies';
$string['competencyupdated'] = 'Competency updated';
$string['configurescale'] = 'Configure scales';
@ -66,7 +67,6 @@ $string['idnumber'] = 'Id number';
$string['invalidpersistent'] = 'Invalid persistent';
$string['itemstoadd'] = 'Items to add';
$string['learningplans'] = 'Learning plans';
$string['linkcompetencies'] = 'Link competencies';
$string['linkcoursecompetencies'] = 'Link course competencies';
$string['linkedcourses'] = 'Linked courses';
$string['linktemplatecompetencies'] = 'Link template competencies';

View file

@ -1,10 +1,12 @@
<div data-region="competencylinktree">
{{^singleFramework}}
<h3>{{#str}}competencyframeworks, tool_lp{{/str}}</h3>
<select data-action="chooseframework">
{{#frameworks}}
<option value="{{id}}" {{#selected}}selected="selected"{{/selected}}>{{shortname}} <em>{{idnumber}}</em></option>
{{/frameworks}}
</select>
{{/singleFramework}}
<h3>{{#str}}locatecompetency, tool_lp{{/str}}</h3>
<form data-region="filtercompetencies" data-frameworkid="{{framework.id}}">

View file

@ -20,6 +20,7 @@
Classes required for JS:
Data attibutes required for JS:
* data-action=add
* data-enhance=tree
Context variables required for this template: