MDL-23187 Generic ratings and comments permissions now appear on course pages, AND...
[moodle.git] / enrol / yui / enrolmentmanager / enrolmentmanager.js
CommitLineData
a70eb30f
SH
1YUI.add('moodle-enrol-enrolmentmanager', function(Y) {
2
3 var UEP = {
4 NAME : 'Enrolment Manager',
5 /** Properties **/
6 BASE : 'base',
7 SEARCH : 'search',
8 PARAMS : 'params',
9 URL : 'url',
10 AJAXURL : 'ajaxurl',
11 MULTIPLE : 'multiple',
12 PAGE : 'page',
13 COURSEID : 'courseid',
14 USERS : 'users',
15 USERCOUNT : 'userCount',
16 REQUIREREFRESH : 'requiresRefresh',
17 LASTSEARCH : 'lastPreSearchValue',
18 INSTANCES : 'instances',
19 OPTIONSTARTDATE : 'optionsStartDate',
20 DEFAULTROLE : 'defaultRole',
21 DEFAULTSTARTDATE : 'defaultStartDate',
22 DEFAULTDURATION : 'defaultDuration',
23 ASSIGNABLEROLES : 'assignableRoles'
24 },
25 /** CSS classes for nodes in structure **/
26 CSS = {
27 PANEL : 'user-enroller-panel',
28 WRAP : 'uep-wrap',
29 HEADER : 'uep-header',
30 CONTENT : 'uep-content',
31 AJAXCONTENT : 'uep-ajax-content',
32 SEARCHRESULTS : 'uep-search-results',
33 TOTALUSERS : 'totalusers',
34 USERS : 'users',
35 USER : 'user',
36 MORERESULTS : 'uep-more-results',
37 LIGHTBOX : 'uep-loading-lightbox',
38 LOADINGICON : 'loading-icon',
39 FOOTER : 'uep-footer',
40 ENROL : 'enrol',
41 ENROLLED : 'enrolled',
42 COUNT : 'count',
43 PICTURE : 'picture',
44 DETAILS : 'details',
45 FULLNAME : 'fullname',
46 EMAIL : 'email',
47 OPTIONS : 'options',
48 ODD : 'odd',
49 EVEN : 'even',
50 HIDDEN : 'hidden',
51 SEARCHOPTIONS : 'uep-searchoptions',
52 COLLAPSIBLEHEADING : 'collapsibleheading',
53 COLLAPSIBLEAREA : 'collapsiblearea',
54 SEARCHOPTION : 'uep-enrolment-option',
55 ROLE : 'role',
56 STARTDATE : 'startdate',
57 DURATION : 'duration',
58 ACTIVE : 'active',
59 SEARCH : 'uep-search',
60 CLOSE : 'close'
61 };
62
63 var USERENROLLER = function(config) {
64 USERENROLLER.superclass.constructor.apply(this, arguments);
65 };
66 Y.extend(USERENROLLER, Y.Base, {
67 _searchTimeout : null,
68 _loadingNode : null,
69 _escCloseEvent : null,
70 initializer : function(config) {
71 this.set(UEP.BASE, Y.Node.create('<div class="'+CSS.PANEL+' '+CSS.HIDDEN+'"></div>')
72 .append(Y.Node.create('<div class="'+CSS.WRAP+'"></div>')
73 .append(Y.Node.create('<div class="'+CSS.HEADER+' header"></div>')
74 .append(Y.Node.create('<div class="'+CSS.CLOSE+'"></div>'))
75 .append(Y.Node.create('<h2>'+M.str.enrol.enrolusers+'</h2>')))
76 .append(Y.Node.create('<div class="'+CSS.CONTENT+'"></div>')
77 .append(Y.Node.create('<div class="'+CSS.AJAXCONTENT+'"></div>'))
78 .append(Y.Node.create('<div class="'+CSS.LIGHTBOX+' '+CSS.HIDDEN+'"></div>')
79 .append(Y.Node.create('<img alt="loading" class="'+CSS.LOADINGICON+'" />')
80 .setAttribute('src', M.util.image_url('i/loading', 'moodle')))
81 .setStyle('opacity', 0.5)))
82 .append(Y.Node.create('<div class="'+CSS.FOOTER+'"></div>')
83 .append(Y.Node.create('<div class="'+CSS.SEARCH+'"><label>'+M.str.enrol.usersearch+'</label></div>')
84 .append(Y.Node.create('<input type="text" id="enrolusersearch" value="" />'))
85 .append(Y.Node.create('<div class="'+CSS.SEARCHOPTION+' '+CSS.ROLE+'">'+M.str.role.assignroles+'</div>')
86 .append(Y.Node.create('<select><option value="">'+M.str.enrol.none+'</option></select>'))
87 )
88 )
89 .append(Y.Node.create('<div class="'+CSS.SEARCHOPTIONS+'"></div>')
90 .append(Y.Node.create('<div class="'+CSS.COLLAPSIBLEHEADING+'"><img alt="" />'+M.str.enrol.enrolmentoptions+'</div>'))
91 .append(Y.Node.create('<div class="'+CSS.COLLAPSIBLEAREA+' '+CSS.HIDDEN+'"></div>')
92 .append(Y.Node.create('<div class="'+CSS.SEARCHOPTION+' '+CSS.STARTDATE+'">'+M.str.moodle.startingfrom+'</div>')
93 .append(Y.Node.create('<select></select>')))
94 .append(Y.Node.create('<div class="'+CSS.SEARCHOPTION+' '+CSS.DURATION+'">'+M.str.enrol.enrolperiod+'</div>')
95 .append(Y.Node.create('<select><option value="0" selected="selected">'+M.str.enrol.unlimitedduration+'</option></select>')))
96 )
97 )
98 )
99 )
100 );
101
102 this.set(UEP.SEARCH, this.get(UEP.BASE).one('#enrolusersearch'));
103 Y.all('.enrolusersbutton input').each(function(node){
104 if (node.getAttribute('type', 'submit')) {
105 node.on('click', this.show, this);
106 }
107 }, this);
108 this.get(UEP.BASE).one('.'+CSS.HEADER+' .'+CSS.CLOSE).on('click', this.hide, this);
109 this._loadingNode = this.get(UEP.BASE).one('.'+CSS.CONTENT+' .'+CSS.LIGHTBOX);
110 var params = this.get(UEP.PARAMS);
111 params['id'] = this.get(UEP.COURSEID);
112 this.set(UEP.PARAMS, params);
113
114 Y.on('key', this.preSearch, this.get(UEP.SEARCH), 'down:13', this);
115
116 Y.one(document.body).append(this.get(UEP.BASE));
117
118 var base = this.get(UEP.BASE);
119 base.plug(Y.Plugin.Drag);
120 base.dd.addHandle('.'+CSS.HEADER+' h2');
121 base.one('.'+CSS.HEADER+' h2').setStyle('cursor', 'move');
122
123
124 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url('t/collapsed', 'moodle'));
125 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).on('click', function(){
126 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).toggleClass(CSS.ACTIVE);
127 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEAREA).toggleClass(CSS.HIDDEN);
128 if (this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEAREA).hasClass(CSS.HIDDEN)) {
129 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url('t/collapsed', 'moodle'));
130 } else {
131 this.get(UEP.BASE).one('.'+CSS.SEARCHOPTIONS+' .'+CSS.COLLAPSIBLEHEADING).one('img').setAttribute('src', M.util.image_url('t/expanded', 'moodle'));
132 }
133 }, this);
134
135 this.populateAssignableRoles();
136 this.populateStartDates();
137 this.populateDuration();
138 },
139 populateAssignableRoles : function() {
140 this.on('assignablerolesloaded', function(){
141 var roles = this.get(UEP.ASSIGNABLEROLES);
142 var s = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.ROLE+' select');
143 var v = this.get(UEP.DEFAULTROLE);
144 var index = 0, count = 0;
145 for (var i in roles) {
146 count++;
147 var option = Y.Node.create('<option value="'+i+'">'+roles[i]+'</option>');
148 if (i == v) {
149 index = count;
150 }
151 s.append(option);
152 }
153 s.set('selectedIndex', index);
154 }, this);
155 this.getAssignableRoles();
156 },
157 populateStartDates : function() {
158 var select = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.STARTDATE+' select');
159 var defaultvalue = this.get(UEP.DEFAULTSTARTDATE);
160 var options = this.get(UEP.OPTIONSTARTDATE);
161 var index = 0, count = 0;
162 for (var i in options) {
163 count++;
164 var option = Y.Node.create('<option value="'+i+'">'+options[i]+'</option>');
165 if (i == defaultvalue) {
166 index = count;
167 }
168 select.append(option);
169 }
170 select.set('selectedIndex', index);
171 },
172 populateDuration : function() {
173 var select = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.DURATION+' select');
174 var defaultvalue = this.get(UEP.DEFAULTDURATION);
175 var index = 0, count = 0;
176 for (var i = 1; i <= 365; i++) {
177 count++;
178 var option = Y.Node.create('<option value="'+i+'">'+M.str.enrol.durationdays.replace(/\%d/, i)+'</option>');
179 if (i == defaultvalue) {
180 index = count;
181 }
182 select.append(option);
183 }
184 select.set('selectedIndex', index);
185 },
186 getAssignableRoles : function(){
187 Y.io(M.cfg.wwwroot+'/enrol/ajax.php', {
188 method:'POST',
189 data:'id='+this.get(UEP.COURSEID)+'&action=getassignable&sesskey='+M.cfg.sesskey,
190 on: {
191 complete: function(tid, outcome, args) {
192 try {
193 var roles = Y.JSON.parse(outcome.responseText);
194 this.set(UEP.ASSIGNABLEROLES, roles.response);
195 } catch (e) {
196 Y.fail(UEP.NAME+': Failed to load assignable roles');
197 }
198 this.getAssignableRoles = function() {
199 this.fire('assignablerolesloaded');
200 }
201 this.getAssignableRoles();
202 }
203 },
204 context:this
205 });
206 },
207 preSearch : function(e) {
208 this.search(null, false);
209 /*
210 var value = this.get(UEP.SEARCH).get('value');
211 if (value.length < 3 || value == this.get(UEP.LASTSEARCH)) {
212 return;
213 }
214 this.set(UEP.LASTSEARCH, value);
215 if (this._searchTimeout) {
216 clearTimeout(this._searchTimeout);
217 this._searchTimeout = null;
218 }
219 var self = this;
220 this._searchTimeout = setTimeout(function(){
221 self._searchTimeout = null;
222 self.search(null, false);
223 }, 300);
224 */
225 },
226 show : function(e) {
227 e.preventDefault();
228 e.halt();
229
230 var base = this.get(UEP.BASE);
231 base.removeClass(CSS.HIDDEN);
232 var x = (base.get('winWidth') - 400)/2;
233 var y = (parseInt(base.get('winHeight'))-base.get('offsetHeight'))/2 + parseInt(base.get('docScrollY'));
234 if (y < parseInt(base.get('winHeight'))*0.1) {
235 y = parseInt(base.get('winHeight'))*0.1;
236 }
237 base.setXY([x,y]);
238
239 if (this.get(UEP.USERS)===null) {
240 this.search(e, false);
241 }
242
243 this._escCloseEvent = Y.on('key', this.hide, document.body, 'down:27', this);
244 },
245 hide : function(e) {
246 if (this._escCloseEvent) {
247 this._escCloseEvent.detach();
248 this._escCloseEvent = null;
249 }
250 this.get(UEP.BASE).addClass(CSS.HIDDEN);
251 if (this.get(UEP.REQUIREREFRESH)) {
252 window.location = this.get(UEP.URL);
253 }
254 },
255 search : function(e, append) {
256 if (e) {
257 e.halt();
258 e.preventDefault();
259 }
260 var on, params;
261 if (append) {
262 this.set(UEP.PAGE, this.get(UEP.PAGE)+1);
263 } else {
264 this.set(UEP.USERCOUNT, 0);
265 }
266 params = this.get(UEP.PARAMS);
267 params['sesskey'] = M.cfg.sesskey;
268 params['action'] = 'searchusers';
269 params['search'] = this.get(UEP.SEARCH).get('value');
270 params['page'] = this.get(UEP.PAGE);
271 if (this.get(UEP.MULTIPLE)) {
272 alert('oh no there are multiple');
273 } else {
274 var instance = this.get(UEP.INSTANCES)[0];
275 params['enrolid'] = instance.id;
276 }
277 Y.io(M.cfg.wwwroot+this.get(UEP.AJAXURL), {
278 method:'POST',
279 data:build_querystring(params),
280 on : {
281 start : this.displayLoading,
282 complete: this.processSearchResults,
283 end : this.removeLoading
284 },
285 context:this,
286 arguments:{
287 append:append,
288 enrolid:params['enrolid']
289 }
290 });
291 },
292 displayLoading : function() {
293 this._loadingNode.removeClass(CSS.HIDDEN);
294 },
295 removeLoading : function() {
296 this._loadingNode.addClass(CSS.HIDDEN);
297 },
298 processSearchResults : function(tid, outcome, args) {
299 try {
300 var result = Y.JSON.parse(outcome.responseText);
301 } catch (e) {
302 Y.fail(UEP.NAME+': Failed to parse user search response ['+e.linenum+':'+e.message+']');
303 }
304 if (!result.success) {
305 this.setContent = M.str.enrol.errajaxsearch;
306 }
307 var users;
308 if (!args.append) {
309 users = Y.Node.create('<div class="'+CSS.USERS+'"></div>');
310 } else {
311 users = this.get(UEP.BASE).one('.'+CSS.SEARCHRESULTS+' .'+CSS.USERS);
312 }
313 var count = this.get(UEP.USERCOUNT);
314 for (var i in result.response.users) {
315 count++;
316 var user = result.response.users[i];
317 users.append(Y.Node.create('<div class="'+CSS.USER+' clearfix" rel="'+user.id+'"></div>')
318 .addClass((i%2)?CSS.ODD:CSS.EVEN)
319 .append(Y.Node.create('<div class="'+CSS.COUNT+'">'+count+'</div>'))
320 .append(Y.Node.create('<div class="'+CSS.PICTURE+'"></div>')
321 .append(Y.Node.create(user.picture)))
322 .append(Y.Node.create('<div class="'+CSS.DETAILS+'"></div>')
323 .append(Y.Node.create('<div class="'+CSS.FULLNAME+'">'+user.fullname+'</div>'))
324 .append(Y.Node.create('<div class="'+CSS.EMAIL+'">'+user.email+'</div>')))
325 .append(Y.Node.create('<div class="'+CSS.OPTIONS+'"></div>')
326 .append(Y.Node.create('<div class="'+CSS.ENROL+'">'+M.str.enrol.enrol+'</div>')))
327 );
328 }
329 this.set(UEP.USERCOUNT, count);
330 if (!args.append) {
331 var usersstr = (result.response.totalusers == '1')?M.str.enrol.ajaxoneuserfound:M.str.enrol.ajaxxusersfound.replace(/\[users\]/, result.response.totalusers);
332 var content = Y.Node.create('<div class="'+CSS.SEARCHRESULTS+'"></div>')
333 .append(Y.Node.create('<div class="'+CSS.TOTALUSERS+'">'+usersstr+'</div>'))
334 .append(users);
335 if (result.response.totalusers > (this.get(UEP.PAGE)+1)*25) {
336 var fetchmore = Y.Node.create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.str.enrol.ajaxnext25+'</a></div>');
337 fetchmore.on('click', this.search, this, true);
338 content.append(fetchmore)
339 }
340 this.setContent(content);
341 Y.delegate("click", this.enrolUser, users, '.'+CSS.USER+' .'+CSS.ENROL, this, args);
342 } else {
343 if (result.response.totalusers <= (this.get(UEP.PAGE)+1)*25) {
344 this.get(UEP.BASE).one('.'+CSS.MORERESULTS).remove();
345 }
346 }
347 },
348 enrolUser : function(e, args) {
349 var user = e.currentTarget.ancestor('.'+CSS.USER);
350 var params = [];
351 params['id'] = this.get(UEP.COURSEID);
352 params['userid'] = user.getAttribute("rel");
353 params['enrolid'] = args.enrolid;
354 params['sesskey'] = M.cfg.sesskey;
355 params['action'] = 'enrol';
356 params['role'] = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.ROLE+' select').get('value');
357 params['startdate'] = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.STARTDATE+' select').get('value');
358 params['duration'] = this.get(UEP.BASE).one('.'+CSS.SEARCHOPTION+'.'+CSS.DURATION+' select').get('value');
359 Y.io(M.cfg.wwwroot+this.get(UEP.AJAXURL), {
360 method:'POST',
361 data:build_querystring(params),
362 on: {
363 start : this.displayLoading,
364 complete : function(tid, outcome, args) {
365 try {
366 var result = Y.JSON.parse(outcome.responseText);
367 } catch (e) {
368 Y.fail(UEP.NAME+': Failed to parse user search response ['+e.linenum+':'+e.message+']');
369 }
370 if (result.success) {
371 args.userNode.addClass(CSS.ENROLLED);
372 args.userNode.one('.'+CSS.ENROL).remove();
373 this.set(UEP.REQUIREREFRESH, true);
374 } else {
375 alert(M.str.enrol.errajaxfailedenrol);
376 }
377 },
378 end : this.removeLoading
379 },
380 context:this,
381 arguments:{
382 params : params,
383 userNode : user
384 }
385 });
386
387 },
388 setContent: function(content) {
389 this.get(UEP.BASE).one('.'+CSS.CONTENT+' .'+CSS.AJAXCONTENT).setContent(content);
390 }
391 }, {
392 NAME : UEP.NAME,
393 ATTRS : {
394 url : {
395 validator : Y.Lang.isString
396 },
397 ajaxurl : {
398 validator : Y.Lang.isString
399 },
400 base : {
401 setter : function(node) {
402 var n = Y.one(node);
403 if (!n) {
404 Y.fail(UEP.NAME+': invalid base node set');
405 }
406 return n;
407 }
408 },
409 users : {
410 validator : Y.Lang.isArray,
411 value : null
412 },
413 courseid : {
414 value : null
415 },
416 params : {
417 validator : Y.Lang.isArray,
418 value : []
419 },
420 instances : {
421 validator : Y.Lang.isArray,
422 setter : function(instances) {
423 var i,ia = [], count=0;
424 for (i in instances) {
425 ia.push(instances[i]);
426 count++;
427 }
428 this.set(UEP.MULTIPLE, (count>1));
429 }
430 },
431 multiple : {
432 validator : Y.Lang.isBool,
433 value : false
434 },
435 page : {
436 validator : Y.Lang.isNumber,
437 value : 0
438 },
439 userCount : {
440 value : 0,
441 validator : Y.Lang.isNumber
442 },
443 requiresRefresh : {
444 value : false,
445 validator : Y.Lang.isBool
446 },
447 search : {
448 setter : function(node) {
449 var n = Y.one(node);
450 if (!n) {
451 Y.fail(UEP.NAME+': invalid search node set');
452 }
453 return n;
454 }
455 },
456 lastPreSearchValue : {
457 value : '',
458 validator : Y.Lang.isString
459 },
460 strings : {
461 value : {},
462 validator : Y.Lang.isObject
463 },
464 defaultRole : {
23cee7a4 465 value : 0
a70eb30f
SH
466 },
467 defaultStartDate : {
468 value : 2,
469 validator : Y.Lang.isNumber
470 },
471 defaultDuration : {
472 value : ''
473 },
474 assignableRoles : {
475 value : []
476 },
477 optionsStartDate : {
478 value : []
479 }
480 }
481 });
482 Y.augment(USERENROLLER, Y.EventTarget);
483
484
485 M.enrol = M.enrol || {};
486 M.enrol.enrolmentmanager = {
487 init : function(cfg) {
488 new USERENROLLER(cfg);
489 }
490 }
491
492}, '@VERSION@', {requires:['base','node', 'overlay', 'io', 'test', 'json-parse', 'event-delegate', 'dd-plugin', 'event-key']});