MDL-48248 enrol_manual: Handle durations more precise than int days
[moodle.git] / enrol / manual / yui / quickenrolment / quickenrolment.js
1 YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
3     var UEP = {
4         NAME : 'Enrolment Manager',
5         /** Properties **/
6         BASE : 'base',
7         SEARCH : 'search',
8         SEARCHBTN : 'searchbtn',
9         PARAMS : 'params',
10         URL : 'url',
11         AJAXURL : 'ajaxurl',
12         MULTIPLE : 'multiple',
13         PAGE : 'page',
14         COURSEID : 'courseid',
15         USERS : 'users',
16         USERCOUNT : 'userCount',
17         REQUIREREFRESH : 'requiresRefresh',
18         LASTSEARCH : 'lastPreSearchValue',
19         INSTANCES : 'instances',
20         OPTIONSTARTDATE : 'optionsStartDate',
21         DEFAULTROLE : 'defaultRole',
22         DEFAULTSTARTDATE : 'defaultStartDate',
23         DEFAULTDURATION : 'defaultDuration',
24         ASSIGNABLEROLES : 'assignableRoles',
25         DISABLEGRADEHISTORY : 'disableGradeHistory',
26         RECOVERGRADESDEFAULT : 'recoverGradesDefault',
27         ENROLCOUNT : 'enrolCount',
28         PERPAGE : 'perPage',
29         COHORTSAVAILABLE : 'cohortsAvailable',
30         COHORTCOUNT : 'cohortCount'
31     };
32     /** CSS classes for nodes in structure **/
33     var CSS = {
34         PANEL : 'user-enroller-panel',
35         WRAP : 'uep-wrap',
36         HEADER : 'uep-header',
37         CONTENT : 'uep-content',
38         AJAXCONTENT : 'uep-ajax-content',
39         SEARCHRESULTS : 'uep-search-results',
40         TOTALUSERS : 'totalusers',
41         USERS : 'users',
42         USER : 'user',
43         MORERESULTS : 'uep-more-results',
44         LIGHTBOX : 'uep-loading-lightbox',
45         LOADINGICON : 'loading-icon',
46         FOOTER : 'uep-footer',
47         ENROL : 'enrol',
48         ENROLLED : 'enrolled',
49         COUNT : 'count',
50         PICTURE : 'picture',
51         DETAILS : 'details',
52         FULLNAME : 'fullname',
53         EXTRAFIELDS : 'extrafields',
54         OPTIONS : 'options',
55         ODD  : 'odd',
56         EVEN : 'even',
57         HIDDEN : 'hidden',
58         RECOVERGRADES : 'recovergrades',
59         RECOVERGRADESTITLE : 'recovergradestitle',
60         SEARCHOPTIONS : 'uep-searchoptions',
61         COLLAPSIBLEHEADING : 'collapsibleheading',
62         COLLAPSIBLEAREA : 'collapsiblearea',
63         ENROLMENTOPTION : 'uep-enrolment-option',
64         SEARCHCONTROLS : 'uep-controls',
65         ROLE : 'role',
66         STARTDATE : 'startdate',
67         DURATION : 'duration',
68         ACTIVE : 'active',
69         SEARCH : 'uep-search',
70         SEARCHBTN : 'uep-search-btn',
71         CLOSE : 'close',
72         CLOSEBTN : 'close-button',
73         ENTITYSELECTOR : 'uep-entity-selector',
74         COHORTS : 'cohorts',
75         COHORT : 'cohort',
76         COHORTNAME : 'cohortname',
77         TOTALCOHORTS : 'totalcohorts'
78     };
79     var create = Y.Node.create;
81     var USERENROLLER = function(config) {
82         USERENROLLER.superclass.constructor.apply(this, arguments);
83     };
84     Y.extend(USERENROLLER, Y.Base, {
85         _searchTimeout : null,
86         _loadingNode : null,
87         _escCloseEvent : null,
88         initializer : function(config) {
89             var recovergrades = null;
90             if (this.get(UEP.DISABLEGRADEHISTORY) != true) {
91                 recovergrades = create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.RECOVERGRADES+'"></div>')
92                     .append(create('<label class="'+CSS.RECOVERGRADESTITLE+'" for="'+CSS.RECOVERGRADES+'">'+M.util.get_string('recovergrades', 'enrol')+'</label>'))
93                     .append(create('<input type="checkbox" id="'+CSS.RECOVERGRADES+'" name="'+CSS.RECOVERGRADES+'"'+ this.get(UEP.RECOVERGRADESDEFAULT) +' />'))
94             }
96             this.set(UEP.BASE, create('<div class="'+CSS.PANEL+' '+CSS.HIDDEN+'"></div>')
97                 .append(create('<div class="'+CSS.WRAP+'"></div>')
98                     .append(create('<div class="'+CSS.HEADER+' header"></div>')
99                         .append(create('<div class="'+CSS.CLOSE+'"></div>'))
100                         .append(create('<h2>'+M.util.get_string('enrolusers', 'enrol')+'</h2>')))
101                     .append(create('<div class="'+CSS.CONTENT+'"></div>')
102                         .append(create('<div class="'+CSS.SEARCHCONTROLS+'"></div>')
103                             .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.ROLE+'"><label for="id_enrol_manual_assignable_roles">'+M.util.get_string('assignroles', 'role')+'</label></div>')
104                                     .append(create('<select id="id_enrol_manual_assignable_roles"><option value="">'+M.util.get_string('none', 'enrol')+'</option></select>'))
105                             )
106                             .append(create('<div class="'+CSS.ENTITYSELECTOR+'"></div>'))
107                             .append(create('<div class="'+CSS.SEARCHOPTIONS+'"></div>')
108                                 .append(create('<div class="'+CSS.COLLAPSIBLEHEADING+'"><img alt="" />'+M.util.get_string('enrolmentoptions', 'enrol')+'</div>'))
109                                 .append(create('<div class="'+CSS.COLLAPSIBLEAREA+' '+CSS.HIDDEN+'"></div>')
110                                     .append(recovergrades)
111                                     .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.STARTDATE+'">'+M.util.get_string('startingfrom', 'moodle')+'</div>')
112                                         .append(create('<select></select>')))
113                                     .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.DURATION+'">'+M.util.get_string('enrolperiod', 'enrol')+'</div>')
114                                         .append(create('<select><option value="0" selected="selected">'+M.util.get_string('unlimitedduration', 'enrol')+'</option></select>')))
115                                 )
116                             )
117                             .append(create('<div class="'+CSS.SEARCH+'"><label for="enrolusersearch" class="accesshide">'+M.util.get_string('usersearch', 'enrol')+'</label></div>')
118                                 .append(create('<input type="text" id="enrolusersearch" value="" />'))
119                                 .append(create('<input type="button" id="searchbtn" class="'+CSS.SEARCHBTN+'" value="'+M.util.get_string('usersearch', 'enrol')+'" />'))
120                             )
121                         )
122                         .append(create('<div class="'+CSS.AJAXCONTENT+'"></div>'))
123                         .append(create('<div class="'+CSS.LIGHTBOX+' '+CSS.HIDDEN+'"></div>')
124                             .append(create('<img alt="loading" class="'+CSS.LOADINGICON+'" />')
125                                 .setAttribute('src', M.util.image_url('i/loading', 'moodle')))
126                             .setStyle('opacity', 0.5)))
127                     .append(create('<div class="'+CSS.FOOTER+'"></div>')
128                         .append(create('<div class="'+CSS.CLOSEBTN+'"></div>')
129                             .append(create('<input type="button" value="'+M.util.get_string('finishenrollingusers', 'enrol')+'" />'))
130                         )
131                     )
132                 )
133             );
135             this.set(UEP.SEARCH, this.get(UEP.BASE).one('#enrolusersearch'));
136             this.set(UEP.SEARCHBTN, this.get(UEP.BASE).one('#searchbtn'));
137             Y.all('.enrol_manual_plugin input').each(function(node){
138                 if (node.getAttribute('type', 'submit')) {
139                     node.on('click', this.show, this);
140                 }
141             }, this);
142             this.get(UEP.BASE).one('.'+CSS.HEADER+' .'+CSS.CLOSE).on('click', this.hide, this);
143             this.get(UEP.BASE).one('.'+CSS.FOOTER+' .'+CSS.CLOSEBTN+' input').on('click', this.hide, this);
144             this._loadingNode = this.get(UEP.BASE).one('.'+CSS.CONTENT+' .'+CSS.LIGHTBOX);
145             var params = this.get(UEP.PARAMS);
146             params['id'] = this.get(UEP.COURSEID);
147             this.set(UEP.PARAMS, params);
149             Y.on('key', this.preSearch, this.get(UEP.SEARCH), 'down:13', this);
150             this.get(UEP.SEARCHBTN).on('click', this.preSearch, this);
152             if (this.get(UEP.COHORTSAVAILABLE)) {
153                 this.get(UEP.BASE).one('.'+CSS.ENTITYSELECTOR)
154                     .append(create('<input type="radio" id="id_enrol_manual_entity_users" name="enrol_manual_entity" value="users" checked="checked"/>'))
155                     .append(create('<label for="id_enrol_manual_entity_users">'+ M.util.get_string('browseusers', 'enrol_manual')+'</label>'))
156                     .append(create('<input type="radio" id="id_enrol_manual_entity_cohorts" name="enrol_manual_entity" value="cohorts"/>'))
157                     .append(create('<label for="id_enrol_manual_entity_cohorts">'+M.util.get_string('browsecohorts', 'enrol_manual')+'</label>'));
158                 this.get(UEP.BASE).one('#id_enrol_manual_entity_cohorts').on('change', this.search, this);
159                 this.get(UEP.BASE).one('#id_enrol_manual_entity_users').on('change', this.search, this);
160             } else {
161                 this.get(UEP.BASE).one('.'+CSS.ENTITYSELECTOR)
162                     .append(create('<input type="hidden" name="enrol_manual_entity" value="users"/>'));
163             }
165             Y.one(document.body).append(this.get(UEP.BASE));
167             var base = this.get(UEP.BASE);
168             base.plug(Y.Plugin.Drag);
169             base.dd.addHandle('.'+CSS.HEADER+' h2');
170             base.one('.'+CSS.HEADER+' h2').setStyle('cursor', 'move');
172             var collapsedimage = 't/collapsed'; // ltr mode
173             if ( Y.one(document.body).hasClass('dir-rtl') ) {
174                 collapsedimage = 't/collapsed_rtl';
175             } else {
176                 collapsedimage = 't/collapsed';
177             }
179             this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url(collapsedimage, 'moodle'));
180             this.populateStartDates();
181             this.populateDuration();
182             this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).on('click', function(){
183                 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).toggleClass(CSS.ACTIVE);
184                 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEAREA).toggleClass(CSS.HIDDEN);
185                 if (this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEAREA).hasClass(CSS.HIDDEN)) {
186                     this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url(collapsedimage, 'moodle'));
187                 } else {
188                     this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url('t/expanded', 'moodle'));
189                 }
190             }, this);
191             this.populateAssignableRoles();
192         },
193         populateAssignableRoles : function() {
194             this.on('assignablerolesloaded', function(){
195                 var roles = this.get(UEP.ASSIGNABLEROLES);
196                 var s = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.ROLE+' select');
197                 var v = this.get(UEP.DEFAULTROLE);
198                 var index = 0, count = 0;
199                 for (var i in roles) {
200                     count++;
201                     var option = create('<option value="'+i+'">'+roles[i]+'</option>');
202                     if (i == v) {
203                         index = count;
204                     }
205                     s.append(option);
206                 }
207                 s.set('selectedIndex', index);
208                 Y.one('#id_enrol_manual_assignable_roles').focus();
209             }, this);
210             this.getAssignableRoles();
211         },
212         populateStartDates : function() {
213             var select = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.STARTDATE+' select');
214             var defaultvalue = this.get(UEP.DEFAULTSTARTDATE);
215             var options = this.get(UEP.OPTIONSTARTDATE);
216             var index = 0, count = 0;
217             for (var i in options) {
218                 var option = create('<option value="'+i+'">'+options[i]+'</option>');
219                 if (i == defaultvalue) {
220                     index = count;
221                 }
222                 select.append(option);
223                 count++;
224             }
225             select.set('selectedIndex', index);
226         },
227         populateDuration : function() {
228             var select = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.DURATION+' select');
229             var defaultvalue = this.get(UEP.DEFAULTDURATION);
230             var prefix = Math.round(defaultvalue) != defaultvalue ? '≈' : '';
231             var index = 0, count = 0;
232             var durationdays = M.util.get_string('durationdays', 'enrol', '{a}');
233             for (var i = 1; i <= 365; i++) {
234                 count++;
235                 var option = create('<option value="'+i+'">'+durationdays.replace('{a}', i)+'</option>');
236                 if (i == defaultvalue) {
237                     index = count;
238                 }
239                 select.append(option);
240             }
241             if (!index && defaultvalue > 0) {
242                 select.append(create('<option value="'+defaultvalue+'">'+durationdays.replace('{a}',
243                     prefix + (Math.round(defaultvalue * 100) / 100))+'</option>'));
244                 index = ++count;
245             }
246             select.set('selectedIndex', index);
247         },
248         getAssignableRoles : function(){
249             Y.io(M.cfg.wwwroot+'/enrol/ajax.php', {
250                 method:'POST',
251                 data:'id='+this.get(UEP.COURSEID)+'&action=getassignable&sesskey='+M.cfg.sesskey,
252                 on: {
253                     complete: function(tid, outcome, args) {
254                         try {
255                             var roles = Y.JSON.parse(outcome.responseText);
256                             this.set(UEP.ASSIGNABLEROLES, roles.response);
257                         } catch (e) {
258                             new M.core.exception(e);
259                         }
260                         this.getAssignableRoles = function() {
261                             this.fire('assignablerolesloaded');
262                         };
263                         this.getAssignableRoles();
264                     }
265                 },
266                 context:this
267             });
268         },
269         preSearch : function(e) {
270             this.search(e, false);
271             /*
272             var value = this.get(UEP.SEARCH).get('value');
273             if (value.length < 3 || value == this.get(UEP.LASTSEARCH)) {
274                 return;
275             }
276             this.set(UEP.LASTSEARCH, value);
277             if (this._searchTimeout) {
278                 clearTimeout(this._searchTimeout);
279                 this._searchTimeout = null;
280             }
281             var self = this;
282             this._searchTimeout = setTimeout(function(){
283                 self._searchTimeout = null;
284                 self.search(null, false);
285             }, 300);
286             */
287         },
288         show : function(e) {
289             e.preventDefault();
290             e.halt();
292             var base = this.get(UEP.BASE);
293             base.removeClass(CSS.HIDDEN);
294             var x = (base.get('winWidth') - 400)/2;
295             var y = (parseInt(base.get('winHeight'))-base.get('offsetHeight'))/2 + parseInt(base.get('docScrollY'));
296             if (y < parseInt(base.get('winHeight'))*0.1) {
297                 y = parseInt(base.get('winHeight'))*0.1;
298             }
299             base.setXY([x,y]);
300             var zindex = 0;
301             Y.all('.moodle-has-zindex').each(function() {
302                 if (parseInt(this.getComputedStyle('zIndex'), 10) > zindex) {
303                     zindex = parseInt(this.getComputedStyle('zIndex'), 10);
304                 }
305             });
306             base.setStyle('zIndex', zindex + 1);
308             if (this.get(UEP.USERS)===null) {
309                 this.search(e, false);
310             }
312             this._escCloseEvent = Y.on('key', this.hide, document.body, 'down:27', this);
313             var rolesselect = Y.one('#id_enrol_manual_assignable_roles');
314             if (rolesselect) {
315                 rolesselect.focus();
316             }
317         },
318         hide : function(e) {
319             if (this._escCloseEvent) {
320                 this._escCloseEvent.detach();
321                 this._escCloseEvent = null;
322             }
323             this.get(UEP.BASE).addClass(CSS.HIDDEN);
324             if (this.get(UEP.REQUIREREFRESH)) {
325                 window.location = this.get(UEP.URL);
326             }
327         },
328         currentEntity : function() {
329             var entity = CSS.USER;
330             var cohortsinput = Y.one('#id_enrol_manual_entity_cohorts');
331             if (cohortsinput && cohortsinput.get('checked')) {
332                 entity = CSS.COHORT;
333             }
334             return entity;
335         },
336         search : function(e, append) {
337             var entity = this.currentEntity();
339             if (e) {
340                 e.halt();
341                 e.preventDefault();
342             }
343             var on, params;
344             if (append) {
345                 this.set(UEP.PAGE, this.get(UEP.PAGE)+1);
346             } else {
347                 this.set(UEP.USERCOUNT, 0);
348                 this.set(UEP.COHORTCOUNT, 0);
349                 this.set(UEP.PAGE, 0);
350             }
351             params = this.get(UEP.PARAMS);
352             params['sesskey'] = M.cfg.sesskey;
353             params['action'] = (entity === CSS.USER) ? 'searchusers' : 'searchcohorts';
354             params['search'] = this.get(UEP.SEARCH).get('value');
355             params['page'] = this.get(UEP.PAGE);
356             params['enrolcount'] = this.get(UEP.ENROLCOUNT);
357             params['perpage'] = this.get(UEP.PERPAGE);
359             if (this.get(UEP.MULTIPLE)) {
360                 alert('oh no there are multiple');
361             } else {
362                 var instance = this.get(UEP.INSTANCES)[0];
363                 params['enrolid'] = instance.id;
364             }
365             Y.io(M.cfg.wwwroot+this.get(UEP.AJAXURL), {
366                 method:'POST',
367                 data:build_querystring(params),
368                 on : {
369                     start : this.displayLoading,
370                     complete: ((entity === CSS.USER) ? this.processSearchResults : this.processCohortsSearchResults),
371                     end : this.removeLoading
372                 },
373                 context:this,
374                 arguments:{
375                     append:append,
376                     enrolid:params['enrolid']
377                 }
378             });
379         },
380         displayLoading : function() {
381             this._loadingNode.removeClass(CSS.HIDDEN);
382         },
383         removeLoading : function() {
384             this._loadingNode.addClass(CSS.HIDDEN);
385         },
386         processSearchResults : function(tid, outcome, args) {
387             try {
388                 var result = Y.JSON.parse(outcome.responseText);
389                 if (result.error) {
390                     return new M.core.ajaxException(result);
391                 }
392             } catch (e) {
393                 new M.core.exception(e);
394             }
395             if (!result.success) {
396                 this.setContent = M.util.get_string('errajaxsearch', 'enrol');
397             }
398             var users;
399             if (!args.append) {
400                 users = create('<div class="'+CSS.USERS+'"></div>');
401             } else {
402                 users = this.get(UEP.BASE).one('.'+CSS.SEARCHRESULTS+' .'+CSS.USERS);
403             }
404             var count = this.get(UEP.USERCOUNT);
405             for (var i in result.response.users) {
406                 count++;
407                 var user = result.response.users[i];
408                 users.append(create('<div class="'+CSS.USER+' clearfix" rel="'+user.id+'"></div>')
409                     .addClass((count%2)?CSS.ODD:CSS.EVEN)
410                     .append(create('<div class="'+CSS.COUNT+'">'+count+'</div>'))
411                     .append(create('<div class="'+CSS.PICTURE+'"></div>')
412                         .append(create(user.picture)))
413                     .append(create('<div class="'+CSS.DETAILS+'"></div>')
414                         .append(create('<div class="'+CSS.FULLNAME+'">'+user.fullname+'</div>'))
415                         .append(create('<div class="'+CSS.EXTRAFIELDS+'">'+user.extrafields+'</div>')))
416                     .append(create('<div class="'+CSS.OPTIONS+'"></div>')
417                         .append(create('<input type="button" class="'+CSS.ENROL+'" value="'+M.util.get_string('enrol', 'enrol')+'" />')))
418                 );
419             }
420             this.set(UEP.USERCOUNT, count);
421             if (!args.append) {
422                 var usersstr = (result.response.totalusers == '1')?M.util.get_string('ajaxoneuserfound', 'enrol'):M.util.get_string('ajaxxusersfound','enrol', result.response.totalusers);
423                 var content = create('<div class="'+CSS.SEARCHRESULTS+'"></div>')
424                     .append(create('<div class="'+CSS.TOTALUSERS+'">'+usersstr+'</div>'))
425                     .append(users);
426                 if (result.response.totalusers > (this.get(UEP.PAGE)+1)*this.get(UEP.PERPAGE)) {
427                     var fetchmore = create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.util.get_string('ajaxnext25', 'enrol')+'</a></div>');
428                     fetchmore.on('click', this.search, this, true);
429                     content.append(fetchmore)
430                 }
431                 this.setContent(content);
432                 Y.delegate("click", this.enrolUser, users, '.'+CSS.USER+' .'+CSS.ENROL, this, args);
433             } else {
434                 if (result.response.totalusers <= (this.get(UEP.PAGE)+1)*this.get(UEP.PERPAGE)) {
435                     this.get(UEP.BASE).one('.'+CSS.MORERESULTS).remove();
436                 }
437             }
438         },
439         processCohortsSearchResults : function(tid, outcome, args) {
440             try {
441                 var result = Y.JSON.parse(outcome.responseText);
442                 if (result.error) {
443                     return new M.core.ajaxException(result);
444                 }
445             } catch (e) {
446                 new M.core.exception(e);
447             }
448             if (!result.success) {
449                 this.setContent = M.util.get_string('errajaxsearch', 'enrol');
450             }
451             var cohorts;
452             if (!args.append) {
453                 cohorts = create('<div class="'+CSS.COHORTS+'"></div>');
454             } else {
455                 cohorts = this.get(UEP.BASE).one('.'+CSS.SEARCHRESULTS+' .'+CSS.COHORTS);
456             }
457             var count = this.get(UEP.COHORTCOUNT);
458             for (var i in result.response.cohorts) {
459                 count++;
460                 var cohort = result.response.cohorts[i];
461                 cohorts.append(create('<div class="'+CSS.COHORT+' clearfix" rel="'+cohort.id+'"></div>')
462                     .addClass((count%2)?CSS.ODD:CSS.EVEN)
463                     .append(create('<div class="'+CSS.COUNT+'">'+count+'</div>'))
464                     .append(create('<div class="'+CSS.DETAILS+'"></div>')
465                         .append(create('<div class="'+CSS.COHORTNAME+'">'+cohort.name+'</div>')))
466                     .append(create('<div class="'+CSS.OPTIONS+'"></div>')
467                         .append(create('<input type="button" class="' + CSS.ENROL + '" value="' + M.util.get_string('enrolxusers', 'enrol', cohort.cnt) + '" />')))
468                 );
469             }
470             this.set(UEP.COHORTCOUNT, count);
471             if (!args.append) {
472                 //var usersstr = (result.response.totalusers == '1')?M.util.get_string('ajaxoneuserfound', 'enrol'):M.util.get_string('ajaxxusersfound','enrol', result.response.totalusers);
473                 var cohortsstr = M.util.get_string('foundxcohorts', 'enrol', result.response.totalcohorts);
474                 var content = create('<div class="'+CSS.SEARCHRESULTS+'"></div>')
475                     .append(create('<div class="'+CSS.TOTALCOHORTS+'">'+cohortsstr+'</div>'))
476                     .append(cohorts);
477                 if (result.response.totalcohorts > (this.get(UEP.PAGE)+1)*this.get(UEP.PERPAGE)) {
478                     var fetchmore = create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.util.get_string('ajaxnext25', 'enrol')+'</a></div>');
479                     fetchmore.on('click', this.search, this, true);
480                     content.append(fetchmore)
481                 }
482                 this.setContent(content);
483                 Y.delegate("click", this.enrolUser, cohorts, '.'+CSS.COHORT+' .'+CSS.ENROL, this, args);
484             } else {
485                 if (result.response.totalcohorts <= (this.get(UEP.PAGE)+1)*this.get(UEP.PERPAGE)) {
486                     this.get(UEP.BASE).one('.'+CSS.MORERESULTS).remove();
487                 }
488             }
489         },
490         enrolUser : function(e, args) {
491             var entityname = this.currentEntity();
493             var entity = e.currentTarget.ancestor('.'+entityname);
494             var params = [];
495             params['id'] = this.get(UEP.COURSEID);
496             if (entityname === CSS.USER) {
497                 params['userid'] = entity.getAttribute("rel");
498             } else {
499                 params['cohortid'] = entity.getAttribute("rel");
500             }
501             params['enrolid'] = args.enrolid;
502             params['sesskey'] = M.cfg.sesskey;
503             params['action'] = 'enrol';
504             params['role'] = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.ROLE+' select').get('value');
505             params['startdate'] = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.STARTDATE+' select').get('value');
506             params['duration'] = this.get(UEP.BASE).one('.'+CSS.ENROLMENTOPTION+'.'+CSS.DURATION+' select').get('value');
507             if (this.get(UEP.DISABLEGRADEHISTORY) != true) {
508                 params['recovergrades'] = this.get(UEP.BASE).one('#'+CSS.RECOVERGRADES).get('checked')?1:0;
509             } else {
510                 params['recovergrades'] = 0;
511             }
513             Y.io(M.cfg.wwwroot+this.get(UEP.AJAXURL), {
514                 method:'POST',
515                 data:build_querystring(params),
516                 on: {
517                     start : this.displayLoading,
518                     complete : function(tid, outcome, args) {
519                         try {
520                             var result = Y.JSON.parse(outcome.responseText);
521                             if (result.error) {
522                                 return new M.core.ajaxException(result);
523                             } else {
524                                 args.entityNode.addClass(CSS.ENROLLED);
525                                 args.entityNode.one('.'+CSS.ENROL).remove();
526                                 this.set(UEP.REQUIREREFRESH, true);
527                                 var countenrol = this.get(UEP.ENROLCOUNT)+1;
528                                 this.set(UEP.ENROLCOUNT, countenrol);
529                             }
530                         } catch (e) {
531                             new M.core.exception(e);
532                         }
533                     },
534                     end : this.removeLoading
535                 },
536                 context:this,
537                 arguments:{
538                     params : params,
539                     entityNode : entity
540                 }
541             });
543         },
544         setContent: function(content) {
545             this.get(UEP.BASE).one('.'+CSS.CONTENT+' .'+CSS.AJAXCONTENT).setContent(content);
546         }
547     }, {
548         NAME : UEP.NAME,
549         ATTRS : {
550             url : {
551                 validator : Y.Lang.isString
552             },
553             ajaxurl : {
554                 validator : Y.Lang.isString
555             },
556             base : {
557                 setter : function(node) {
558                     var n = Y.one(node);
559                     if (!n) {
560                         Y.fail(UEP.NAME+': invalid base node set');
561                     }
562                     return n;
563                 }
564             },
565             users : {
566                 validator : Y.Lang.isArray,
567                 value : null
568             },
569             courseid : {
570                 value : null
571             },
572             params : {
573                 validator : Y.Lang.isArray,
574                 value : []
575             },
576             instances : {
577                 validator : Y.Lang.isArray,
578                 setter : function(instances) {
579                     var i,ia = [], count=0;
580                     for (i in instances) {
581                         ia.push(instances[i]);
582                         count++;
583                     }
584                     this.set(UEP.MULTIPLE, (count>1));
585                 }
586             },
587             multiple : {
588                 validator : Y.Lang.isBool,
589                 value : false
590             },
591             page : {
592                 validator : Y.Lang.isNumber,
593                 value : 0
594             },
595             userCount : {
596                 value : 0,
597                 validator : Y.Lang.isNumber
598             },
599             requiresRefresh : {
600                 value : false,
601                 validator : Y.Lang.isBool
602             },
603             search : {
604                 setter : function(node) {
605                     var n = Y.one(node);
606                     if (!n) {
607                         Y.fail(UEP.NAME+': invalid search node set');
608                     }
609                     return n;
610                 }
611             },
612             lastPreSearchValue : {
613                 value : '',
614                 validator : Y.Lang.isString
615             },
616             strings  : {
617                 value : {},
618                 validator : Y.Lang.isObject
619             },
620             defaultRole : {
621                 value : 0
622             },
623             defaultStartDate : {
624                 value : 4,
625                 validator : Y.Lang.isNumber
626             },
627             defaultDuration : {
628                 value : ''
629             },
630             assignableRoles : {
631                 value : []
632             },
633             optionsStartDate : {
634                 value : []
635             },
636             disableGradeHistory : {
637                 value : 0
638             },
639             recoverGradesDefault : {
640                 value : ''
641             },
642             enrolCount : {
643                 value : 0,
644                 validator : Y.Lang.isNumber
645             },
646             perPage : {
647                 value: 25,
648                 Validator: Y.Lang.isNumber
649             },
650             cohortCount : {
651                 value : 0,
652                 validator : Y.Lang.isNumber
653             },
654             cohortsAvailable : {
655                 value : null
656             }
657         }
658     });
659     Y.augment(USERENROLLER, Y.EventTarget);
661     M.enrol_manual = M.enrol_manual || {};
662     M.enrol_manual.quickenrolment = {
663         init : function(cfg) {
664             new USERENROLLER(cfg);
665         }
666     }
668 }, '@VERSION@', {requires:['base','node', 'overlay', 'io-base', 'test', 'json-parse', 'event-delegate', 'dd-plugin', 'event-key', 'moodle-core-notification']});