MDL-50608 tool_lp: tidy up and bug fixes
[moodle.git] / admin / tool / lp / amd / src / competencies.js
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16 /**
17  * Handle add/remove competency links.
18  *
19  * @module     tool_lp/competencies
20  * @package    tool_lp
21  * @copyright  2015 Damyon Wiese <damyon@moodle.com>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
24 define(['jquery',
25         'core/notification',
26         'core/ajax',
27         'core/templates',
28         'tool_lp/dialogue',
29         'core/str',
30         'tool_lp/tree',
31         'tool_lp/dragdrop-reorder'],
32        function($, notification, ajax, templates, Dialogue, str, Ariatree, dragdrop) {
34     /**
35      * Constructor
36      *
37      * @param {Number} itemid
38      * @param {String} itemtype
39      */
40     var competencies = function(itemid, itemtype) {
41         this.itemid = itemid;
42         this.itemtype = itemtype;
43         this.selectedCompetency = 0;
44         var localthis = this;
45         var loadframeworks = ajax.call([
46             { methodname: 'tool_lp_list_competency_frameworks', args: { filters: {}, sort: 'sortorder' } }
47         ]);
49         loadframeworks[0].done(function(frameworks) {
50             localthis.frameworks = frameworks;
51             if (frameworks.length === 0) {
52                 templates.render('tool_lp/no_frameworks_warning', {})
53                     .done(function(html) {
54                         $('[data-region="actions"]').append(html);
55                         $('[data-region="actions"] button').hide();
56                     }).fail(notification.exception);
57                 return;
58             }
59             $('[data-region="actions"] button').show();
60             localthis.registerEvents();
61             localthis.registerDragDrop();
62         }).fail(notification.exception);
63     };
65     /**
66      * Initialise the drag/drop code.
67      * @method registerDragDrop
68      */
69     competencies.prototype.registerDragDrop = function() {
70         var localthis = this;
71         // Init this module.
72         str.get_string('movecompetency', 'tool_lp').done(
73             function(movestring) {
74                 dragdrop.dragdrop('movecompetency',
75                                   movestring,
76                                   { identifier: 'movecompetency', component: 'tool_lp'},
77                                   { identifier: 'movecompetencyafter', component: 'tool_lp'},
78                                   'drag-samenode',
79                                   'drag-parentnode',
80                                   'drag-handlecontainer',
81                                   function(drag, drop) {
82                                       localthis.handleDrop.call(localthis, drag, drop);
83                                   });
84             }
85         ).fail(notification.exception);
87     };
89     /**
90      * Handle a drop from a drag/drop operation.
91      *
92      * @method handleDrop
93      * @param {Node} drag The dragged node.
94      * @param {Node} drop The dropped on node.
95      */
96     competencies.prototype.handleDrop = function(drag, drop) {
97         var fromid = $(drag).data('id');
98         var toid = $(drop).data('id');
99         var localthis = this;
100         var requests = [];
102         if (localthis.itemtype == 'course') {
103             requests = ajax.call([
104                 {
105                     methodname: 'tool_lp_reorder_course_competency',
106                     args: { courseid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid }
107                 }
108             ]);
109         } else if (localthis.itemtype == 'template') {
110             requests = ajax.call([
111                 {
112                     methodname: 'tool_lp_reorder_template_competency',
113                     args: { templateid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid }
114                 }
115             ]);
116         } else {
117             return null;
118         }
120         requests[0].fail(notification.exception);
121     };
123     /**
124      * Get the search text from the input field and reload the tree based on the search.
125      *
126      * @method applyFilter
127      * @param {Event} e The event that triggered the button.
128      */
129     competencies.prototype.applyFilter = function(e) {
130         e.preventDefault();
131         var localthis = this;
132         var searchInput = $('[data-region="filtercompetencies"] input');
133         var searchText = searchInput.val();
134         var framework = $('[data-action="chooseframework"]');
135         var frameworkid = framework.val();
137         this.searchCompetencies().done(function (competencies) {
138             var i = 0;
140             var framework = localthis.frameworks[0];
141             for (i = 0; i < localthis.frameworks.length; i++) {
142                 if (localthis.frameworks[i].id == frameworkid) {
143                     framework = localthis.frameworks[i];
144                     framework.selected = true;
145                 } else {
146                     localthis.frameworks[i].selected = false;
147                 }
148             }
149             framework.selected = true;
150             var context = {
151                 framework: framework,
152                 frameworks: localthis.frameworks,
153                 competencies: competencies,
154                 search: searchText
155             };
156             templates.render('tool_lp/link_competencies', context).done(function(html) {
157                 $('[data-region="competencylinktree"]').replaceWith(html);
158                 localthis.initLinkCompetencies();
159             }).fail(notification.exception);
160         }).fail(notification.exception);
161     };
163     /**
164      * The link course competencies popup was just opened and we need to initialise it.
165      *
166      * @method initLinkCompetencies
167      */
168     competencies.prototype.initLinkCompetencies = function() {
169         var localthis = this;
171         new Ariatree('[data-enhance=linktree]', function(target) {
172             localthis.selectedCompetency = target.data('id');
173         });
175         $('[data-action="chooseframework"]').change(function(e) {
176             return localthis.applyFilter.call(localthis, e);
177         });
179         $('[data-region="filtercompetencies"] button').click(function(e) {
180             $(e.target).attr('disabled', 'disabled');
181             return localthis.applyFilter.call(localthis, e);
182         });
184         $('[data-region="competencylinktree"] [data-action="cancel"]').click(function(e) {
185             $(e.target).attr('disabled', 'disabled');
186             e.preventDefault();
187             localthis.popup.close();
188         });
189         $('[data-region="competencylinktree"] [data-action="add"]').click(function(e) {
190             var requests = [],
191                 pagerender = '',
192                 pageregion = '';
194             e.preventDefault();
195             if (!localthis.selectedCompetency) {
196                 return;
197             }
199             $(e.target).attr('disabled', 'disabled');
201             // Add the link and reload the page template.
202             if (localthis.itemtype == 'course') {
203                 requests = ajax.call([
204                     { methodname: 'tool_lp_add_competency_to_course',
205                       args: { courseid: localthis.itemid, competencyid: localthis.selectedCompetency } },
206                     { methodname: 'tool_lp_data_for_course_competencies_page',
207                       args: { courseid: localthis.itemid } }
208                 ]);
209                 pagerender = 'tool_lp/course_competencies_page';
210                 pageregion = 'coursecompetenciespage';
211             } else if (localthis.itemtype == 'template') {
212                 requests = ajax.call([
213                     { methodname: 'tool_lp_add_competency_to_template',
214                         args: { templateid: localthis.itemid, competencyid: localthis.selectedCompetency } },
215                     { methodname: 'tool_lp_data_for_template_competencies_page',
216                         args: { templateid: localthis.itemid } }
217                 ]);
218                 pagerender = 'tool_lp/template_competencies_page';
219                 pageregion = 'templatecompetenciespage';
220             } else {
221                 return null;
222             }
224             requests[1].done(function(context) {
225                 templates.render(pagerender, context).done(function(html, js) {
226                     localthis.popup.close();
227                     $('[data-region="' + pageregion + '"]').replaceWith(html);
228                     templates.runTemplateJS(js);
229                 }).fail(notification.exception);
230             }).fail(notification.exception);
231         });
232     };
234     /**
235      * Register the javascript event handlers for this page.
236      *
237      * @method registerEvents
238      */
239     competencies.prototype.registerEvents = function() {
240         var localthis = this;
241         $('[data-region="actions"] button').click(function(e) {
242             return localthis.openCompetencySelector.call(localthis, e);
243         });
244         $('[data-action="delete-competency-link"]').click(function(e) {
245             var requests = [],
246                 pagerender = '',
247                 pageregion = '';
249             e.preventDefault();
251             var deleteid = $(e.target).closest('[data-id]').data('id');
253             // Delete the link and reload the page template.
254             if (localthis.itemtype == 'course') {
255                 requests = ajax.call([
256                     { methodname: 'tool_lp_remove_competency_from_course',
257                       args: { courseid: localthis.itemid, competencyid: deleteid } },
258                     { methodname: 'tool_lp_data_for_course_competencies_page',
259                       args: { courseid: localthis.itemid } }
260                 ]);
261                 pagerender = 'tool_lp/course_competencies_page';
262                 pageregion = 'coursecompetenciespage';
263             } else if (localthis.itemtype == 'template') {
264                 requests = ajax.call([
265                     { methodname: 'tool_lp_remove_competency_from_template',
266                         args: { templateid: localthis.itemid, competencyid: deleteid } },
267                     { methodname: 'tool_lp_data_for_template_competencies_page',
268                         args: { templateid: localthis.itemid } }
269                 ]);
270                 pagerender = 'tool_lp/template_competencies_page';
271                 pageregion = 'templatecompetenciespage';
272             }
274             requests[1].done(function(context) {
275                 templates.render(pagerender, context).done(function(html, js) {
276                     $('[data-region="' + pageregion + '"]').replaceWith(html);
277                     templates.runTemplateJS(js);
278                 }).fail(notification.exception);
279             }).fail(notification.exception);
280         });
281     };
283     /**
284      * Turn the flat list of competencies into a tree.
285      *
286      * @method addCompetencyChildren
287      * @param {Object} parent The current parent node
288      * @param {Object[]} competencies The flat list of all nodes.
289      */
290     competencies.prototype.addCompetencyChildren = function(parent, competencies) {
291         var i;
293         for (i = 0; i < competencies.length; i++) {
294             if (competencies[i].parentid == parent.id) {
295                 parent.haschildren = true;
296                 competencies[i].children = [];
297                 competencies[i].haschildren = false;
298                 parent.children[parent.children.length] = competencies[i];
299                 this.addCompetencyChildren(competencies[i], competencies);
300             }
301         }
302     };
304     /**
305      * Get the search text from the input, and reload the tree.
306      *
307      * @method searchCompetencies
308      * @return {promise} When resolved it will contain the tree of competencies.
309      */
310     competencies.prototype.searchCompetencies = function() {
311         var localthis = this;
312         var deferred = $.Deferred();
313         var searchInput = $('[data-region="filtercompetencies"] input');
314         var searchText = '';
315         if (searchInput.length) {
316             searchText = searchInput.val();
317         }
318         var framework = $('[data-action="chooseframework"]');
319         var frameworkid = localthis.frameworks[0].id;
320         if (framework.length) {
321             frameworkid = framework.val();
322         }
324         var loadcompetencies = ajax.call([
325             { methodname: 'tool_lp_search_competencies', args: { searchtext: searchText, competencyframeworkid: frameworkid } }
326         ]);
328         loadcompetencies[0].done(function (competencies) {
329             // Expand the list of competencies into a tree.
330             var i, competenciestree = [];
331             for (i = 0; i < competencies.length; i++) {
332                 var onecompetency = competencies[i];
333                 if (onecompetency.parentid === "0") {
334                     onecompetency.children = [];
335                     onecompetency.haschildren = 0;
336                     competenciestree[competenciestree.length] = onecompetency;
337                     localthis.addCompetencyChildren(onecompetency, competencies);
338                 }
339             }
340             deferred.resolve(competenciestree);
341         }).fail(function (ex) { deferred.reject(ex); });
343         return deferred.promise();
344     };
346     /**
347      * Open a popup to choose competencies.
348      *
349      * @method openCompetencySelector
350      */
351     competencies.prototype.openCompetencySelector = function(e) {
352         e.preventDefault();
353         var localthis = this;
355         this.searchCompetencies().done(function (competencies) {
356             var framework = localthis.frameworks[0];
357             framework.selected = true;
358             var context = { framework: framework, frameworks: localthis.frameworks, competencies: competencies, search: '' };
359             templates.render('tool_lp/link_competencies', context).done(function(html) {
360                 str.get_string('linkcompetencies', 'tool_lp').done(function(title) {
361                     localthis.popup = new Dialogue(
362                         title,
363                         html, // The link UI.
364                         function() {
365                             localthis.initLinkCompetencies.call(localthis);
366                         }
367                     );
368                 }).fail(notification.exception);
369             }).fail(notification.exception);
370         }).fail(notification.exception);
371     };
373     return competencies;
374 });