fd8c9a3de94a4d5f85bc957f3be0a03913aae4e3
[Evergreen.git] / Evergreen / xul / staff_client / server / patron / ue.js
1 var cgi                                                 = null;
2 var clone                                               = false;
3 var patron                                              = null;
4 var counter                                             = 0;
5 var identTypesCache                     = {};
6 var statCatsCache                               = {};
7 var surveysCache                                = {};
8 var surveyQuestionsCache        = {};
9 var surveyAnswersCache          = {};
10 var userCache                                   = {};
11 var groupsCache                         = {};
12 var netLevelsCache                      = {};
13 //var guardianNote                              = null; 
14
15 /* fetch the necessary data to start off */
16 function uEditInit() {
17
18         _debug('uEditInit(): ' + location.search);
19
20         cgi             = new CGI();
21         session = cgi.param('ses');
22         clone           = cgi.param('clone');
23         if(!session) throw "User session is not defined";
24
25         fetchUser(session);
26         $('uedit_user').appendChild(text(USER.usrname()));
27
28         setTimeout( function() { 
29                 uEditBuild(); uEditShowPage('uedit_userid'); }, 20 );
30
31    /*
32    window.onunload = function() { 
33       if( ! confirm($('ue_unsaved_changes').innerHTML) ) 
34          location.href = location.href;
35    }
36    */
37
38
39 }
40
41 function uEditSetUnload() {
42    window.onbeforeunload = function(evt) { 
43       //evt.returnValue = ('ue_unsaved_changes').innerHTML; 
44       return $('ue_unsaved_changes').innerHTML; 
45    };
46 }
47
48 function uEditClearUnload() {
49    window.onbeforeunload = null;
50 }
51
52 /* ------------------------------------------------------------------------------ */
53 /* Fetch code
54 /* ------------------------------------------------------------------------------ */
55 function uEditFetchIdentTypes() {
56         _debug("uEditFetchIdentTypes()");
57         var req = new Request(FETCH_ID_TYPES);
58         req.send(true);
59         return req.result();
60 }
61
62 function uEditFetchStatCats() {
63         _debug("uEditFetchStatCats()");
64         var req = new Request(SC_FETCH_ALL, SESSION);
65         req.send(true);
66         return req.result();
67 }
68
69 function uEditFetchSurveys() {
70         _debug("uEditFetchSurveys()");
71         var req = new Request(SV_FETCH_ALL, SESSION);
72         req.send(true);
73         return req.result();
74 }
75
76 function uEditFetchGroups() {
77         _debug("uEditFetchGroups()");
78         var req = new Request(FETCH_GROUPS);
79         req.send(true);
80         return req.result();
81 }
82
83 function uEditFetchNetLevels() {
84         _debug("uEditFetchNetLevels()");
85         var req = new Request(FETCH_NET_LEVELS, SESSION);
86         req.send(true);
87         return req.result();
88 }
89
90 /* ------------------------------------------------------------------------------ */
91
92
93
94 /* fetches necessary and builds the UI */
95 function uEditBuild() {
96         fetchHighestPermOrgs( SESSION, USER.id(), myPerms );
97
98         uEditBuildLibSelector();
99         patron = fetchFleshedUser(cgi.param('usr'));
100         if(!patron) patron = uEditNewPatron(); 
101         
102         uEditDraw( 
103                 uEditFetchIdentTypes(),
104                 uEditFetchGroups(),
105                 uEditFetchStatCats(),
106                 uEditFetchSurveys(),
107                 uEditFetchNetLevels()
108                 );
109
110         if(patron.isnew()) {
111                 if(clone) uEditClone(clone);
112                 else uEditCreateNewAddr();
113
114         } else {
115
116                 /* do we need to display the parent / gurdian field? */
117                 uEditCheckDOB(uEditFindFieldByKey('dob'));
118
119                 $('ue_barcode').disabled = true;
120                 unHideMe($('ue_mark_card_lost'));
121                 unHideMe($('ue_reset_pw'));
122                 uEditCheckEditPerm();
123         }
124
125         if(PERMS['BAR_PATRON'] == -1) 
126                 $('ue_barred').disabled = true;
127 }
128
129
130 /* if this user does not have permission to put users into
131         the edited users group, they do not have permission to 
132         edit this user */
133 function uEditCheckEditPerm() {
134
135         var perm = uEditFindGroupPerm(groupsCache[patron.profile()]);   
136         /*
137         _debug("editing user with group app perm "+patron.profile()+' : '+
138                 groupsCache[patron.profile()].name() +', and perm = ' + perm);
139                 */
140
141         if(PERMS[perm] != -1) return;
142
143         /* we can edit our own account, but not others in our group */
144         if( patron.id() != USER.id() ){
145                 _debug("we are not allowed to edit this user");
146         
147                 $('ue_save').disabled = true;
148                 $('ue_save_clone').disabled = true;
149                 $('ue_mark_card_lost').disabled = true;
150                 $('ue_reset_pw').disabled = true;
151         
152                 uEditIterateFields(
153                         function(f) {
154                                 if( f && f.widget && f.widget.node )
155                                         f.widget.node.disabled = true;
156                         }       
157                 );      
158
159         }
160
161         var node = $('ue_profile').parentNode;
162         node.removeChild($('ue_profile'));
163         node.appendChild(elem('span',null,groupsCache[patron.profile()].name()));
164
165         var field = uEditFindFieldByKey('profile');
166         field.required = false;
167         removeCSSClass(field.widget.node, CSS_INVALID_DATA);
168         uEditCheckErrors();
169 }
170
171
172 /* creates a new patron object with card attached */
173 var uEditCardVirtId = -1;
174 function uEditNewPatron() {
175         var patron = new au(); 
176         patron.isnew(1);
177         patron.id(-1);
178         card = new ac();
179         card.id(uEditCardVirtId--);
180         card.isnew(1);
181         patron.card(card);
182         patron.cards([card]);
183         patron.stat_cat_entries([]);
184         patron.survey_responses([]);
185         patron.addresses([]);
186         patron.home_ou(USER.ws_ou());
187         uEditMakeRandomPw(patron);
188         return patron;
189 }
190
191 function uEditMakeRandomPw(patron) {
192         var rand  = Math.random();
193         rand = parseInt(rand * 10000) + '';
194         while(rand.length < 4) rand += '0';
195         appendClear($('ue_password_plain'),text(rand));
196         unHideMe($('ue_password_gen'));
197         patron.passwd(rand);
198         return rand;
199 }
200
201 function uEditResetPw() { 
202         var pw = uEditMakeRandomPw(patron);     
203         $('ue_password1').value = pw;
204         $('ue_password2').value = pw;
205 }
206
207 function uEditClone(clone) {
208
209         var cloneUser = fetchFleshedUser(clone);
210         patron.usrgroup(cloneUser.usrgroup());
211
212         if( cloneUser.day_phone() )
213                 $('ue_day_phone').value = cloneUser.day_phone();
214         if( cloneUser.evening_phone() )
215                 $('ue_night_phone').value = cloneUser.evening_phone();
216         if( cloneUser.other_phone() )
217                 $('ue_other_phone').value = cloneUser.other_phone();
218         setSelector($('ue_org_selector'), cloneUser.home_ou());
219
220
221         setSelector($('ue_profile'), cloneUser.profile());
222
223         /* force the expire date to be set */
224         $('ue_profile').onchange();
225
226         for( var a in cloneUser.addresses() ) {
227                 var addr = cloneUser.addresses()[a];
228                 if( cloneUser.mailing_address && 
229                                 addr.id() == cloneUser.mailing_address().id() )
230                         patron.mailing_address(addr);
231                 if( cloneUser.billing_address() &&
232                                 addr.id() == cloneUser.billing_address().id() )
233                         patron.billing_address(addr);
234                 patron.addresses().push(addr);
235         }
236
237         uEditBuildAddrs(patron);
238 }
239
240
241 /* Creates a new blank address, 
242         adds it to the user and the fields array */
243 var uEditVirtualAddrId = -1;
244 function uEditCreateNewAddr() {
245         var addr = new aua();
246
247         addr.id(uEditVirtualAddrId--);
248         addr.isnew(1);
249         addr.usr(patron.id());
250         addr.country(defaultCountry);
251
252         if(!patron.addresses()) 
253                 patron.addresses([]);
254
255         if(patron.addresses().length == 0) {
256                 patron.mailing_address(addr);
257                 patron.billing_address(addr);
258         }
259
260         addr.valid(1);
261         addr.within_city_limits(1);
262
263         uEditBuildAddrFields(patron, addr);
264         patron.addresses().push(addr);
265         uEditIterateFields(function(f) { uEditCheckValid(f); });
266         uEditCheckErrors();
267 }
268
269
270 /* kicks off the UI drawing */
271 function uEditDraw(identTypes, groups, statCats, surveys, netLevels ) {
272         hideMe($('uedit_loading'));
273         unHideMe($('ue_maintd'));
274
275         dataFields = [];
276         uEditDrawIDTypes(identTypes);
277         uEditDrawGroups(groups, null, null, true);
278         uEditDrawStatCats(statCats);
279         uEditDrawSurveys(surveys);
280         uEditDrawNetLevels(netLevels);
281         uEditDefineData(patron);
282
283         uEditIterateFields(function(f) { uEditActivateField(f) });
284         uEditIterateFields(function(f) { uEditCheckValid(f); });
285         uEditCheckErrors();
286 }
287
288
289 /** Applies the event handlers and sets the data for the field */
290 function uEditActivateField(field) {
291
292         if( field.widget.id ) {
293                 field.widget.node = $(field.widget.id);
294
295         } else {
296                 field.widget.node = 
297                         $n(field.widget.base, field.widget.name);
298         }
299
300         uEditSetOnchange(field);
301
302         if(field.widget.onblur) {
303                 field.widget.node.onblur = 
304                         function() { field.widget.onblur(field); };
305         }
306
307         field.widget.node.disabled = field.widget.disabled;
308         if(field.object == null) return;
309         var val = field.object[field.key]();
310         if(val == null) return;
311
312         if( field.widget.type == 'input' )
313                 field.widget.node.value = val;
314
315         if( field.widget.type == 'select' )
316                 setSelector(field.widget.node, val);
317
318         if( field.widget.type == 'checkbox' )
319                 field.widget.node.checked = 
320                         (val && val != 'f') ? true : false;
321
322         if( field.widget.onload ) 
323                 field.widget.onload(val);
324 }
325
326
327 /* set up the onchange event for the field */
328 function uEditSetOnchange(field) {
329         var func = function() {uEditOnChange( field );}
330         field.widget.node.onchange = func;
331
332         if(field.widget.type != 'select')
333                 field.widget.node.onkeyup = func;
334 }
335
336 /* find the current value of the field object's widget */
337 function uEditNodeVal(field) {
338         if(field.widget.type == 'input')
339                 return field.widget.node.value;
340
341         if(field.widget.type == 'checkbox')
342                 return field.widget.node.checked;
343
344         if(field.widget.type == 'select')
345                 return getSelectorVal(field.widget.node);
346 }
347
348
349 /* update a field value */
350 function uEditOnChange(field) {
351
352         var newval = uEditNodeVal(field);
353         field.object[field.key](newval);
354         field.object.ischanged(1);
355
356         if(field.widget.onpostchange)
357                 field.widget.onpostchange(field, newval);
358
359         //_debug(field.key+' = '+newval);
360
361         uEditIterateFields(function(f) { uEditCheckValid(f); });
362         uEditCheckErrors();
363
364    uEditSetUnload();
365 }
366
367
368 function uEditCheckValid(field) {
369         var newval = uEditNodeVal(field);
370
371         if(newval) {
372
373                 if(field.widget.regex) { 
374                         if(newval.match(field.widget.regex)) 
375                                 removeCSSClass(field.widget.node, CSS_INVALID_DATA);
376                         else
377                                 addCSSClass(field.widget.node, CSS_INVALID_DATA);
378
379                 } else {
380                         removeCSSClass(field.widget.node, CSS_INVALID_DATA);
381                 }
382
383         } else {
384
385                 if(field.required) {
386                         addCSSClass(field.widget.node, CSS_INVALID_DATA);
387
388                 } else {
389                         removeCSSClass(field.widget.node, CSS_INVALID_DATA);
390                 }
391         }
392
393 }
394
395 /* find a field object by object key */
396 function uEditFindFieldByKey(key) {
397         var fields = grep( dataFields,
398                 function(item) { return (item.key == key); });
399         return (fields) ? fields[0] : null;
400 }
401
402 /* find a list of fields by object key */
403 function uEditFindFieldsByKey(key) {
404         return grep( dataFields,
405                 function(item) { return (item.key == key); });
406 }
407
408 /* find a field object by widget id */
409 function uEditFindFieldByWId(id) {
410         var fields = grep( dataFields,
411                 function(item) { return (item.widget.id == id); });
412         return (fields) ? fields[0] : null;
413 }
414
415
416 function uEditIterateFields(callback) {
417         for( var f in dataFields ) 
418                 callback(dataFields[f]);
419 }
420
421
422 function uEditGetErrorStrings() {
423         var errors = [];
424         uEditIterateFields(
425                 function(field) { 
426                         if(field.errkey) {
427                                 if( !field.object.isdeleted() ) {
428                                         if( field.widget.node.className.indexOf(CSS_INVALID_DATA) != -1) {
429                                                 var str = $(field.errkey).innerHTML;
430                                                 if(str) errors.push(str);
431                                         }
432                                 }
433                         }
434                 }
435         );
436
437         /* munge up something for all of the required surveys 
438                 (which are not registered with the fields) */
439         if( patron.isnew() ) {
440                 var sel = $('ue_survey_table');
441
442                 if( sel ) {
443                         var rows = sel.getElementsByTagName('tr');
444
445                         for( var r in rows ) {
446                 
447                                 var row = rows[r];
448                                 var sel = $n(row, 'ue_survey_answer');
449                                 if(!sel) continue;
450                                 var qstn = row.getAttribute('question');
451                 
452                                 if(qstn) {
453                                         qstn            = surveyQuestionsCache[qstn];
454                                         survey  = surveysCache[qstn.survey()];
455                                         var val = getSelectorVal(sel);
456                                         if(!val && isTrue(survey.required()))
457                                                 errors.push($('ue_bad_survey').innerHTML + ' : ' + qstn.question());
458                                 }
459                         }
460                 }
461         }
462
463         /* ------------------------------------------------------------ */
464
465         if(errors[0]) return errors;
466         return null;
467 }
468
469 function uEditAlertErrors() {
470         var errors = uEditGetErrorStrings();
471         if(!errors) return false;
472         alert(errors.join("\n"));
473         return true;
474 }
475
476
477 /* send the user to the database */
478 function uEditSaveUser(cloneme) {
479
480         if(uEditGetErrorStrings()) {
481                 uEditAlertErrors();
482                 return;
483         }
484
485         /* null is unique in the db, but '' is not */
486         if( ! patron.ident_value() ) patron.ident_value(null);
487         //if( ! patron.ident_type2() ) patron.ident_type2(null);
488         if( ! patron.ident_value2() ) patron.ident_value2(null);
489         patron.ident_type2(null);
490
491         if(! patron.dob() ) patron.dob(null);
492
493         _debug("Saving patron with card: " + js2JSON(patron.card()));
494         _debug("Saving full patron: " + js2JSON(patron));
495
496         //for( var c in patron
497
498         var req = new Request(UPDATE_PATRON, SESSION, patron);
499         req.alertEvent = false;
500         req.send(true);
501         var newuser = req.result();
502
503    uEditClearUnload();
504
505         var evt;
506         if( (evt = checkILSEvent(newuser)) || ! newuser ) {
507                 if(evt) {
508                         var j = js2JSON(newuser); 
509                         alert(j);
510                         _debug("USER UPDATE FAILED:\n" + j);
511                 }
512                 return;
513         } 
514
515         alert($('ue_success').innerHTML);
516
517         if(cloneme) {
518                 /* if the user we just created was a clone, and we want to clone it,
519                 we really want to clone the original */
520                 if( clone ) cloneme = clone;
521                 else cloneme = newuser.id();
522         }
523
524
525         if( cloneme ) {
526
527                 if(window.xulG &&
528                         typeof window.xulG.spawn_editor == 'function' && 
529
530                         !patron.isnew() ) {
531                                 _debug("xulG clone spawning new interface...");
532                                 window.xulG.spawn_editor({ses:cgi.param('ses'),clone:cloneme});
533                                 uEditRefresh();
534
535                 } else {
536
537                         var href = location.href;
538                         href = href.replace(/\&?usr=\d+/, '');
539                         href = href.replace(/\&?clone=\d+/, '');
540                         href += '&clone=' + cloneme;
541                         location.href = href;
542                 }
543
544         } else {
545
546                 uEditRefresh();
547         }
548
549         uEditRefreshXUL(newuser);
550 }
551
552
553 function uEditRefreshXUL(newuser) {
554         if (window.xulG && typeof window.xulG.on_save == 'function') 
555                 window.xulG.on_save(newuser);
556 }
557
558 function uEditRefresh() {
559         var href = location.href;
560         href = href.replace(/\&?clone=\d+/, '');
561         location.href = href;
562 }
563
564
565 function uEditCancel() {
566         var href = location.href;
567         href = href.replace(/\&?usr=\d+/, '');
568         href = href.replace(/\&?clone=\d+/, '');
569         var id = cgi.param('usr')
570         /* reload the current user if available */
571         if( id ) href += "&usr=" + id;
572         location.href = href;
573 }
574
575
576 var uEditDupHashes = {};
577 var uEditDupTemplate;
578
579 function uEditRunDupeSearch(type, search_hash) {
580
581         if(!patron.isnew()) return;
582
583         _debug('dup search: ' + js2JSON(search_hash));
584
585         var req = new Request(PATRON_SEARCH, SESSION, search_hash);
586
587         var container = $('dup_div_container');
588         if(!uEditDupTemplate)
589                 uEditDupTemplate = container.removeChild($('dup_div'));
590
591         /* clear any existing dups for this type */
592         iterate( container.getElementsByTagName('div'),
593                 function(d) {
594                         if( d.getAttribute('type') == type ) {
595                                 container.removeChild(d)
596                                 return;
597                         }
598                 }
599         );
600
601         req.callback(
602                 function(r) {
603                         uEditHandleDupResults( r.getResultObject(), search_hash, type, container );
604                 }
605         );
606         req.send();
607 }
608
609
610 function uEditHandleDupResults(ids, search_hash, type, container) {
611
612         _debug('dup search results: ' + js2JSON(ids));
613
614         if(!(ids && ids[0]))  /* no results */
615                 return uEditDupHashes[type] = null;
616
617         /* add a dup link to the UI and plug in the data */
618         var node = uEditDupTemplate.cloneNode(true);
619         container.appendChild(node);
620         node.setAttribute('type', type);
621
622         var link = $n(node, 'link');
623         link.setAttribute('type', type);
624         unHideMe(link);
625         $n(node,'count').appendChild(text(ids.length));
626
627         for( var o in search_hash ) 
628                 $n(node, 'data').appendChild(
629                         text(search_hash[o].value + ' '));
630
631         uEditDupHashes[type] = search_hash;
632
633         switch(type) {
634                 case 'ident' :
635                         if(confirm($('ue_dup_ident1').innerHTML)) 
636                                 uEditShowSearch(null, type);
637                         break;
638         }
639 }
640
641
642 function uEditShowSearch(link,type) {
643         if(!type) type = link.getAttribute('type');
644         if(window.xulG)
645                 window.xulG.spawn_search(uEditDupHashes[type]); 
646         else alert('Search would be:\n' + js2JSON(uEditDupHashes[type]));
647 }
648
649 function uEditMarkCardLost() {
650
651         for( var c in patron.cards() ) {
652
653                 var card = patron.cards()[c];
654                 if( patron.card().id() == card.id() ) {
655
656                         /* de-activite the current card */
657                         card.ischanged(1);
658                         card.active(0);
659
660                         if( !card.barcode() ) {
661                                 /* a card exists in the array with no barcode */
662                                 ueRemoveCard(card.id());
663
664                         } else if( card.isnew() && card.active() == 0 ) {
665                                 /* a new card was created, then never used, removing.. */
666                                 _debug("removing new inactive card "+card.barcode());
667                                 ueRemoveCard(card.id());
668                         }
669
670                         /* create a new card for the patron */
671                         var newcard = new ac();
672                         newcard.id(uEditCardVirtId--);
673                         newcard.isnew(1);
674                         patron.card(newcard);
675                         patron.cards().push(newcard);
676
677
678                         /* reset the widget */
679                         var field = uEditFindFieldByWId('ue_barcode');
680                         field.widget.node.disabled = false;
681                         field.widget.node.value = "";
682                         field.widget.node.onchange();
683                         field.object = newcard;
684                         _debug("uEditMarkCardLost(): created new card object for user");
685                 }
686         }
687 }
688
689
690 function ueRemoveCard(id) {
691         _debug("removing card from cards() array: " + id);
692         var cds = grep( patron.cards(), function(c){return (c.id() != id)});
693         if(!cds) cds = [];
694         for( var j = 0; j < cds.length; j++ )
695                 _debug("patron card array now has :  "+cds[j].id());
696         patron.cards(cds);
697 }
698
699
700
701 function compactArray(arr) {
702         var a = [];
703         for( var i = 0; arr && i < arr.length; i++ ) {
704                 if( arr[i] != null )
705                         a.push(arr[i]);
706         }
707         return a;
708 }