1 dojo.require('dojo.data.ItemFileReadStore');
2 dojo.require('dijit.form.Form');
3 dojo.require('dijit.form.Textarea');
4 dojo.require('dijit.form.FilteringSelect');
5 dojo.require('dijit.form.ComboBox');
6 dojo.require('dijit.form.NumberSpinner');
7 dojo.require('fieldmapper.IDL');
8 dojo.require('openils.PermaCrud');
9 dojo.require('openils.widget.AutoGrid');
10 dojo.require('openils.widget.AutoFieldWidget');
11 dojo.require('dijit.form.CheckBox');
12 dojo.require('dijit.form.Button');
13 dojo.require('dojo.date');
14 dojo.require('openils.CGI');
15 dojo.require('openils.XUL');
16 dojo.require('openils.Util');
17 dojo.require('openils.Event');
19 dojo.requireLocalization('openils.actor', 'register');
20 var localeStrings = dojo.i18n.getLocalization('openils.actor', 'register');
24 var fmClasses = ['au', 'ac', 'aua', 'actsc', 'asv', 'asvq', 'asva'];
31 var uEditUsePhonePw = false;
33 var uEditCardVirtId = -1;
34 var uEditAddrVirtId = -1;
36 var userSettings = {};
37 var userSettingsToUpdate = {};
48 var dupeUsrname = false;
49 var dupeBarcode = false;
51 if(!window.xulG) var xulG = null;
55 staff = new openils.User().user;
56 pcrud = new openils.PermaCrud();
57 cgi = new openils.CGI();
58 cloneUser = cgi.param('clone');
59 var userId = cgi.param('usr');
60 var stageUname = cgi.param('stage');
63 if(xulG.ses) openils.User.authtoken = xulG.ses;
64 if(typeof xulG.clone != 'undefined') cloneUser = xulG.clone;
65 if(typeof xulG.usr != 'undefined') userId = xulG.usr
66 if(typeof xulG.params != 'undefined') {
67 var parms = xulG.params;
68 if(typeof parms.ses != 'undefined')
69 openils.User.authtoken = parms.ses;
70 if(typeof parms.clone != 'undefined')
71 cloneUser = parms.clone;
72 if(typeof parms.usr != 'undefined')
74 if(typeof parms.stage != 'undefined')
75 stageUname = parms.stage
79 orgSettings = fieldmapper.aou.fetchOrgSettingBatch(staff.ws_ou(), [
80 'global.password_regex',
81 'global.juvenile_age_threshold',
82 'patron.password.use_phone',
83 'ui.patron.default_inet_access_level',
84 'circ.holds.behind_desk_pickup_supported'
88 orgSettings[k] = orgSettings[k].value;
90 uEditUsePhonePw = orgSettings['patron.password.use_phone'];
91 uEditFetchUserSettings(userId);
94 patron = uEditLoadUser(userId);
97 patron = uEditLoadStageUser(stageUname);
99 patron = uEditNewPatron();
101 uEditCopyCloneData(patron);
106 var list = pcrud.search('fdoc', {fm_class:fmClasses});
109 if(!fieldDoc[doc.fm_class()])
110 fieldDoc[doc.fm_class()] = {};
111 fieldDoc[doc.fm_class()][doc.field()] = doc;
114 tbody = dojo.byId('uedit-tbody');
116 addrTemplateRows = dojo.query('tr[type=addr-template]', tbody);
117 dojo.forEach(addrTemplateRows, function(row) { row.parentNode.removeChild(row); } );
118 statCatTemplate = tbody.removeChild(dojo.byId('stat-cat-row-template'));
119 surveyTemplate = tbody.removeChild(dojo.byId('survey-row-template'));
120 surveyQuestionTemplate = tbody.removeChild(dojo.byId('survey-question-row-template'));
123 if(patron.isnew() && patron.addresses().length == 0)
124 uEditNewAddr(null, uEditAddrVirtId, true);
128 checkClaimsReturnCountPerm();
129 checkClaimsNoCheckoutCountPerm();
131 dojo.connect(replaceBarcode, 'onClick', replaceCardHandler);
132 dojo.connect(allCards, 'onClick', drawAllCards);
133 if(patron.cards().length > 1)
134 dojo.removeClass(dojo.byId('uedit-all-barcodes'), 'hidden');
138 function drawAllCards() {
140 var tbody = dojo.byId('uedit-all-cards-tbody');
141 if(!allCardsTemplate) {
142 allCardsTemplate = tbody.removeChild(dojo.byId('uedit-all-cards-tr-template'));
144 while(tbody.childNodes[0])
145 tbody.removeChild(tbody.childNodes[0]);
150 [patron.card()].concat(patron.cards()), // grab the main card first
153 if(card.id() == patron.card().id())
156 var row = allCardsTemplate.cloneNode(true);
157 getByName(row, 'barcode').innerHTML = card.barcode();
158 getByName(row, 'active').appendChild(
159 openils.Util.isTrue(card.active()) ?
160 dojo.byId('true').cloneNode(true) :
161 dojo.byId('false').cloneNode(true)
164 tbody.appendChild(row);
169 allCardsDialog.show();
173 * Mark the current card inactive, create a new primary card
175 function replaceCardHandler() {
176 var input = findWidget('ac', 'barcode');
177 input.widget.attr('value', null);
179 // pull old car off the cards list so we don't have a dupe sitting in there
180 var old = patron.cards().filter(function(c){return (c.id() == patron.card().id())})[0];
184 var newc = new fieldmapper.ac();
185 newc.id(uEditCardVirtId--);
189 patron.cards().push(newc);
194 * Loads a staged user and turns them into something the editor can understand
196 function uEditLoadStageUser(stageUname) {
198 var data = fieldmapper.standardRequest(
199 ['open-ils.actor', 'open-ils.actor.user.stage.retrieve.by_username'],
200 { params : [openils.User.authtoken, stageUname] }
203 stageUser = data.user;
204 patron = uEditNewPatron();
209 // copy the data into our new user object
210 for(var key in fieldmapper.IDL.fmclasses.stgu.field_map) {
211 if(fieldmapper.IDL.fmclasses.au.field_map[key] && !fieldmapper.IDL.fmclasses.stgu.field_map[key].virtual) {
212 if(data.user[key]() !== null)
213 patron[key]( data.user[key]() );
217 // copy the data into our new address objects
218 // TODO: uses the first mailing address only
219 if(data.mailing_addresses.length) {
221 var mail_addr = new fieldmapper.aua();
222 mail_addr.id(-1); // virtual ID
225 patron.mailing_address(mail_addr);
226 patron.addresses().push(mail_addr);
228 for(var key in fieldmapper.IDL.fmclasses.stgma.field_map) {
229 if(fieldmapper.IDL.fmclasses.aua.field_map[key] && !fieldmapper.IDL.fmclasses.stgma.field_map[key].virtual) {
230 if(data.mailing_addresses[0][key]() !== null)
231 mail_addr[key]( data.mailing_addresses[0][key]() );
236 // copy the data into our new address objects
237 // TODO uses the first billing address only
238 if(data.billing_addresses.length) {
240 var bill_addr = new fieldmapper.aua();
241 bill_addr.id(-2); // virtual ID
244 patron.billing_address(bill_addr);
245 patron.addresses().push(bill_addr);
247 for(var key in fieldmapper.IDL.fmclasses.stgba.field_map) {
248 if(fieldmapper.IDL.fmclasses.aua.field_map[key] && !fieldmapper.IDL.fmclasses.stgba.field_map[key].virtual) {
249 if(data.billing_addresses[0][key]() !== null)
250 bill_addr[key]( data.billing_addresses[0][key]() );
255 // TODO: uses the first card only
256 if(data.cards.length) {
257 var card = new fieldmapper.ac();
258 card.id(-1); // virtual ID
259 patron.card().barcode(data.cards[0].barcode());
266 * clone the home org, phone numbers, and billing/mailing address
268 function uEditCopyCloneData(patron) {
269 cloneUserObj = uEditLoadUser(cloneUser);
280 patron[field](cloneUserObj[field]());
284 // don't grab all addresses(). the only ones we can link to are billing/mailing
285 if(patron.billing_address())
286 patron.addresses().push(patron.billing_address());
288 if(patron.mailing_address() && (
289 patron.addresses().length == 0 ||
290 patron.mailing_address().id() != patron.billing_address().id()) )
291 patron.addresses().push(patron.mailing_address());
295 function uEditFetchUserSettings(userId) {
297 var baseNode = fieldmapper.aou.findOrgUnit(staff.ws_ou());
298 var orgs = fieldmapper.aou.orgNodeTrail(baseNode);
299 orgs = orgs.map(function(node) { return node.id(); });
301 /* fetch any user setting types we need + any that offer opt-in */
302 userSettingTypes = pcrud.search('cust', {
304 {name:['circ.holds_behind_desk']},
307 select : {atevdef : ['opt_in_setting']},
309 // we only care about opt-in settings for event_defs our users encounter
310 where : {'+atevdef' : {owner : orgs}}
316 var names = userSettingTypes.map(function(obj) { return obj.name() });
318 /* fetch any values set for this user */
319 userSettings = fieldmapper.standardRequest(
320 ['open-ils.actor', 'open-ils.actor.patron.settings.retrieve'],
321 {params : [openils.User.authtoken, userId, names]});
325 function uEditLoadUser(userId) {
326 var patron = fieldmapper.standardRequest(
327 ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve'],
328 {params : [openils.User.authtoken, userId]}
330 openils.Event.parse_and_raise(patron);
334 function loadStaticFields() {
335 for(var idx = 0; tbody.childNodes[idx]; idx++) {
336 var row = tbody.childNodes[idx];
337 if(row.nodeType != row.ELEMENT_NODE) continue;
338 var fmcls = row.getAttribute('fmclass');
340 fleshFMRow(row, fmcls);
343 if(row.id == 'uedit-settings-divider') {
345 var template = tbody.removeChild(dojo.byId('uedit-user-setting-template'));
346 dojo.forEach(userSettingTypes, function(type) { uEditDrawSettingRow(tbody, row, template, type); } );
348 if(userSettingTypes.length > 1 || orgSettings['circ.holds.behind_desk_pickup_supported']) {
349 openils.Util.show('uedit-settings-divider', 'table-row');
356 function uEditDrawSettingRow(tbody, dividerRow, template, stype) {
357 var row = template.cloneNode(true);
358 row.setAttribute('user_setting', stype.name());
359 getByName(row, 'label').innerHTML = stype.label();
360 var cb = new dijit.form.CheckBox({}, getByName(row, 'widget'));
361 cb.attr('value', userSettings[stype.name()]);
362 dojo.connect(cb, 'onChange', function(newVal) { userSettingsToUpdate[stype.name()] = newVal; });
363 tbody.insertBefore(row, dividerRow.nextSibling);
364 openils.Util.show(row, 'table-row');
367 function uEditUpdateUserSettings(userId) {
368 return fieldmapper.standardRequest(
369 ['open-ils.actor', 'open-ils.actor.patron.settings.update'],
370 {params : [openils.User.authtoken, userId, userSettingsToUpdate]});
373 function loadAllAddrs() {
374 dojo.forEach(patron.addresses(),
376 uEditNewAddr(null, addr.id());
381 function loadStatCats() {
383 statCats = fieldmapper.standardRequest(
384 ['open-ils.circ', 'open-ils.circ.stat_cat.actor.retrieve.all'],
385 {params : [openils.User.authtoken, staff.ws_ou()]}
389 for(var idx in statCats) {
390 var stat = statCats[idx];
391 var row = statCatTemplate.cloneNode(true);
392 row.id = 'stat-cat-row-' + idx;
393 tbody.appendChild(row);
394 getByName(row, 'name').innerHTML = stat.name();
395 var valtd = getByName(row, 'widget');
396 var span = valtd.appendChild(document.createElement('span'));
397 var store = new dojo.data.ItemFileReadStore(
398 {data:fieldmapper.actsc.toStoreData(stat.entries())});
399 var comboBox = new dijit.form.ComboBox({store:store}, span);
400 comboBox.labelAttr = 'value';
401 comboBox.searchAttr = 'value';
403 comboBox._wtype = 'statcat';
404 comboBox._statcat = stat.id();
405 widgetPile.push(comboBox);
407 // populate existing cats
408 var map = patron.stat_cat_entries().filter(
409 function(mp) { return (mp.stat_cat() == stat.id()) })[0];
410 if(map) comboBox.attr('value', map.stat_cat_entry());
415 function loadSurveys() {
417 surveys = fieldmapper.standardRequest(
418 ['open-ils.circ', 'open-ils.circ.survey.retrieve.all'],
419 {params : [openils.User.authtoken]}
423 for(var idx in surveys) {
424 var survey = surveys[idx];
425 var srow = surveyTemplate.cloneNode(true);
426 tbody.appendChild(srow);
427 getByName(srow, 'name').innerHTML = survey.name();
429 for(var q in survey.questions()) {
430 var quest = survey.questions()[q];
431 var qrow = surveyQuestionTemplate.cloneNode(true);
432 tbody.appendChild(qrow);
433 getByName(qrow, 'question').innerHTML = quest.question();
435 var span = getByName(qrow, 'answers').appendChild(document.createElement('span'));
436 var store = new dojo.data.ItemFileReadStore(
437 {data:fieldmapper.asva.toStoreData(quest.answers())});
438 var select = new dijit.form.FilteringSelect({store:store}, span);
439 select.labelAttr = 'answer';
440 select.searchAttr = 'answer';
442 select._wtype = 'survey';
443 select._survey = survey.id();
444 select._question = quest.id();
445 widgetPile.push(select);
451 function fleshFMRow(row, fmcls, args) {
452 var fmfield = row.getAttribute('fmfield');
453 var wclass = row.getAttribute('wclass');
454 var wstyle = row.getAttribute('wstyle');
455 var wconstraints = row.getAttribute('wconstraints');
457 var isPasswd2 = (fmfield == 'passwd2');
458 if(isPasswd2) fmfield = 'passwd';
459 var fieldIdl = fieldmapper.IDL.fmclasses[fmcls].field_map[fmfield];
462 var existing = dojo.query('td', row);
463 var htd = existing[0] || row.appendChild(document.createElement('td'));
464 var ltd = existing[1] || row.appendChild(document.createElement('td'));
465 var wtd = existing[2] || row.appendChild(document.createElement('td'));
467 openils.Util.addCSSClass(htd, 'uedit-help');
468 if(fieldDoc[fmcls] && fieldDoc[fmcls][fmfield]) {
469 var link = dojo.byId('uedit-help-template').cloneNode(true);
471 link.onclick = function() { ueLoadContextHelp(fmcls, fmfield) };
472 openils.Util.removeCSSClass(link, 'hidden');
473 htd.appendChild(link);
476 if(!ltd.textContent) {
477 var span = document.createElement('span');
478 ltd.appendChild(document.createTextNode(fieldIdl.label));
481 span = document.createElement('span');
482 wtd.appendChild(span);
485 var disabled = false;
487 case 'au' : fmObject = patron; break;
488 case 'ac' : fmObject = patron.card(); break;
490 fmObject = patron.addresses().filter(
491 function(i) { return (i.id() == args.addr) })[0];
492 if(fmObject && fmObject.usr() != patron.id())
497 var required = row.getAttribute('required') == 'required';
499 // password data is not fetched/required/displayed for existing users
500 if(!patron.isnew() && 'passwd' == fmfield)
506 constraints : (wconstraints) ? eval('('+wconstraints+')') : {}, // the ()'s prevent Invalid Label errors with eval
510 if(fmcls == 'au' && fmfield == 'passwd') {
511 if (orgSettings['global.password_regex']) {
512 dijitArgs.regExp = orgSettings['global.password_regex'];
516 // TODO RSN: Add Setting!
517 if(fmcls == 'au' && fmfield == 'dob')
518 dijitArgs.popupClass = "";
520 var value = row.getAttribute('wvalue');
522 dijitArgs.value = value;
524 // fetch profile groups non-async so existing expire_date is
525 // not overwritten when the profile groups arrive and update
526 var sync = (fmfield == 'profile') ? true : false;
528 var widget = new openils.widget.AutoFieldWidget({
534 widgetClass : wclass,
535 dijitArgs : dijitArgs,
536 orgDefaultsToWs : true,
537 orgLimitPerms : ['UPDATE_USER'],
542 // now put it back before we register the widget
543 if(isPasswd2) fmfield = 'passwd2';
545 widget._wtype = fmcls;
546 widget._fmfield = fmfield;
547 widget._addr = args.addr;
548 widgetPile.push(widget);
549 attachWidgetEvents(fmcls, fmfield, widget);
553 function findWidget(wtype, fmfield, callback) {
554 return widgetPile.filter(
556 if(i._wtype == wtype && i._fmfield == fmfield) {
557 if(callback) return callback(i);
565 * if the user does not have the UPDATE_PATRON_CLAIM_RETURN_COUNT,
566 * they are not allowed to directly alter the claim return count.
567 * This function checks the perm and disable/enables the widget.
569 function checkClaimsReturnCountPerm() {
570 new openils.User().getPermOrgList(
571 'UPDATE_PATRON_CLAIM_RETURN_COUNT',
573 var cr = findWidget('au', 'claims_returned_count');
574 if(orgList.indexOf(patron.home_ou()) == -1)
575 cr.widget.attr('disabled', true);
577 cr.widget.attr('disabled', false);
585 function checkClaimsNoCheckoutCountPerm() {
586 new openils.User().getPermOrgList(
587 'UPDATE_PATRON_CLAIM_NEVER_CHECKED_OUT_COUNT',
589 var cr = findWidget('au', 'claims_never_checked_out_count');
590 if(orgList.indexOf(patron.home_ou()) == -1)
591 cr.widget.attr('disabled', true);
593 cr.widget.attr('disabled', false);
601 function attachWidgetEvents(fmcls, fmfield, widget) {
604 if(fmfield == 'barcode') {
605 dojo.connect(widget.widget, 'onChange',
607 var barcode = this.attr('value');
609 dojo.addClass(dojo.byId('uedit-dupe-barcode-warning'), 'hidden');
610 fieldmapper.standardRequest(
611 ['open-ils.actor', 'open-ils.actor.barcode.exists'],
613 params: [openils.User.authtoken, barcode],
614 oncomplete : function(r) {
615 var res = openils.Util.readResponse(r);
618 dojo.removeClass(dojo.byId('uedit-dupe-barcode-warning'), 'hidden');
621 dojo.addClass(dojo.byId('uedit-dupe-barcode-warning'), 'hidden');
622 var un = findWidget('au', 'usrname');
623 if(!un.widget.attr('value'))
624 un.widget.attr('value', barcode);
639 dojo.connect(widget.widget, 'onChange',
641 var input = findWidget('au', 'usrname');
642 var usrname = input.widget.attr('value');
646 dojo.addClass(dojo.byId('uedit-dupe-username-warning'), 'hidden');
650 fieldmapper.standardRequest(
651 ['open-ils.actor', 'open-ils.actor.username.exists'],
653 params: [openils.User.authtoken, usrname],
654 oncomplete : function(r) {
655 var res = openils.Util.readResponse(r);
658 dojo.removeClass(dojo.byId('uedit-dupe-username-warning'), 'hidden');
661 dojo.addClass(dojo.byId('uedit-dupe-username-warning'), 'hidden');
671 case 'profile': // when the profile changes, update the expire date
672 dojo.connect(widget.widget, 'onChange',
675 var expireWidget = findWidget('au', 'expire_date');
676 function found(items) {
677 if(items.length == 0) return;
679 var interval = self.store.getValue(item, 'perm_interval');
680 expireWidget.widget.attr('value', dojo.date.add(new Date(),
681 'second', openils.Util.intervalToSeconds(interval)));
683 this.store.fetch({onComplete:found, query:{id:this.attr('value')}});
689 dojo.connect(widget.widget, 'onChange',
692 var oldDob = patron.dob();
693 if(dojo.date.stamp.fromISOString(oldDob) == newDob) return;
695 var juvInterval = orgSettings['global.juvenile_age_threshold'] || '18 years';
696 var juvWidget = findWidget('au', 'juvenile');
697 var base = new Date();
698 base.setTime(base.getTime() - Number(openils.Util.intervalToSeconds(juvInterval) + '000'));
700 if(newDob <= base) // older than global.juvenile_age_threshold
701 juvWidget.widget.attr('value', false);
703 juvWidget.widget.attr('value', true);
708 case 'first_given_name':
710 dojo.connect(widget.widget, 'onChange',
711 function(newVal) { uEditDupeSearch('name', newVal); });
715 dojo.connect(widget.widget, 'onChange',
716 function(newVal) { uEditDupeSearch('email', newVal); });
721 dojo.connect(widget.widget, 'onChange',
722 function(newVal) { uEditDupeSearch('ident', newVal); });
726 // if configured, use the last for digits of the day phone number as the password
727 if(uEditUsePhonePw && patron.isnew()) {
728 dojo.connect(widget.widget, 'onChange',
730 if(newVal && newVal.length >= 4) {
731 var pw1 = findWidget('au', 'passwd').widget;
732 var pw2 = findWidget('au', 'passwd2').widget;
733 pw1.attr('value', newVal.substring(newVal.length - 4));
734 pw2.attr('value', newVal.substring(newVal.length - 4));
739 case 'evening_phone':
741 dojo.connect(widget.widget, 'onChange',
742 function(newVal) { uEditDupeSearch('phone', newVal); });
746 dojo.connect(widget.widget, 'onChange',
748 checkClaimsReturnCountPerm();
749 checkClaimsNoCheckoutCountPerm();
755 dojo.connect(widget.widget, 'onChange',
757 var pw1 = findWidget('au', 'passwd').widget;
758 var pw2 = findWidget('au', 'passwd2').widget;
759 var preserved_value = pw2.attr('value');
760 // Ensure that the pw2 field match the pw1 field to validate
761 pw2.regExp = newVal.replace(/([.\\^$*+?\(\)\[\]\{\}])/g, '\\$1');
763 pw2.attr('value',preserved_value);
769 if(fmclass = 'aua') {
772 dojo.connect(widget.widget, 'onChange',
774 fieldmapper.standardRequest(
775 ['open-ils.search', 'open-ils.search.zip'],
778 oncomplete : function(r) {
779 var res = openils.Util.readResponse(r);
781 var callback = function(w) { return w._addr == widget._addr; };
782 if(res.city) findWidget('aua', 'city', callback).widget.attr('value', res.city);
783 if(res.state) findWidget('aua', 'state', callback).widget.attr('value', res.state);
784 if(res.county) findWidget('aua', 'county', callback).widget.attr('value', res.county);
785 if(res.alert) alert(res.alert);
796 dojo.connect(widget.widget, 'onChange',
798 var callback = function(w) { return w._addr == widget._addr; };
800 street1 : findWidget('aua', 'street1', callback).widget.attr('value'),
801 street2 : findWidget('aua', 'street2', callback).widget.attr('value'),
802 city : findWidget('aua', 'city', callback).widget.attr('value'),
803 post_code : findWidget('aua', 'post_code', callback).widget.attr('value')
805 if(args.street1 && args.city && args.post_code)
806 uEditDupeSearch('address', args);
814 function uEditDupeSearch(type, value) {
820 openils.Util.hide('uedit-dupe-names-link');
821 var fname = findWidget('au', 'first_given_name').widget.attr('value');
822 var lname = findWidget('au', 'family_name').widget.attr('value');
823 if( !(fname && lname) ) return;
825 first_given_name : {value : fname, group : 0},
826 family_name : {value : lname, group : 0},
831 openils.Util.hide('uedit-dupe-email-link');
832 search = {email : {value : value, group : 0}};
836 openils.Util.hide('uedit-dupe-ident-link');
837 search = {ident : {value : value, group : 2}};
841 openils.Util.hide('uedit-dupe-phone-link');
842 search = {phone : {value : value, group : 2}};
846 openils.Util.hide('uedit-dupe-address-link');
848 dojo.forEach(['street1', 'street2', 'city', 'post_code'],
851 search[field] = {value : value[field], group: 1};
857 // find possible duplicate patrons
858 fieldmapper.standardRequest(
859 ['open-ils.actor', 'open-ils.actor.patron.search.advanced'],
861 params: [openils.User.authtoken, search],
862 oncomplete : function(r) {
863 var resp = openils.Util.readResponse(r);
864 resp = resp.filter(function(id) { return (id != patron.id()); });
866 if(resp && resp.length > 0) {
868 openils.Util.hide('uedit-help-div');
869 openils.Util.show('uedit-dupe-div');
874 link = dojo.byId('uedit-dupe-names-link');
875 link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_NAME, [resp.length]);
878 link = dojo.byId('uedit-dupe-email-link');
879 link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_EMAIL, [resp.length]);
882 link = dojo.byId('uedit-dupe-ident-link');
883 link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_IDENT, [resp.length]);
886 link = dojo.byId('uedit-dupe-phone-link');
887 link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_PHONE, [resp.length]);
890 link = dojo.byId('uedit-dupe-address-link');
891 link.innerHTML = dojo.string.substitute(localeStrings.DUPE_PATRON_ADDR, [resp.length]);
895 openils.Util.show(link);
896 link.onclick = function() {
897 search.search_sort = js2JSON(["penalties", "family_name", "first_given_name"]);
899 window.xulG.spawn_search(search);
901 console.log("running XUL patron search " + js2JSON(search));
909 function getByName(node, name) {
910 return dojo.query('[name='+name+']', node)[0];
914 function ueLoadContextHelp(fmcls, fmfield) {
915 openils.Util.hide('uedit-dupe-div');
916 openils.Util.show('uedit-help-div');
917 dojo.byId('uedit-help-field').innerHTML = fieldmapper.IDL.fmclasses[fmcls].field_map[fmfield].label;
918 dojo.byId('uedit-help-text').innerHTML = fieldDoc[fmcls][fmfield].string();
922 /* creates a new patron object with card attached */
923 function uEditNewPatron() {
928 card.id(uEditCardVirtId--);
932 patron.cards([card]);
933 patron.net_access_level(orgSettings['ui.patron.default_inet_access_level'] || 1);
934 patron.stat_cat_entries([]);
935 patron.survey_responses([]);
936 patron.addresses([]);
937 uEditMakeRandomPw(patron);
941 function uEditMakeRandomPw(patron) {
942 if(uEditUsePhonePw) return;
943 var rand = Math.random();
944 rand = parseInt(rand * 10000) + '';
945 while(rand.length < 4) rand += '0';
947 appendClear($('ue_password_plain'),text(rand));
948 unHideMe($('ue_password_gen'));
954 function uEditWidgetVal(w) {
955 var val = (w.getFormattedValue) ? w.getFormattedValue() : w.attr('value');
956 if(val === '') val = null;
960 function uEditSave() { _uEditSave(); }
961 function uEditSaveClone() { _uEditSave(true); }
963 function _uEditSave(doClone) {
965 if ( (! myForm.isValid()) || dupeUsrname || dupeBarcode ) {
966 alert('Form is invalid. Please edit and try again.');
970 for(var idx in widgetPile) {
971 var w = widgetPile[idx];
972 var val = uEditWidgetVal(w);
976 if(w._fmfield != 'passwd2')
977 patron[w._fmfield](val);
981 patron.card()[w._fmfield](val);
985 var addr = patron.addresses().filter(function(i){return (i.id() == w._addr)})[0];
987 addr = new fieldmapper.aua();
990 addr.usr(patron.id());
991 patron.addresses().push(addr);
993 if(addr[w._fmfield]() != val)
996 addr[w._fmfield](val);
998 if(dojo.byId('uedit-billing-address-' + addr.id()).checked)
999 patron.billing_address(addr.id());
1001 if(dojo.byId('uedit-mailing-address-' + addr.id()).checked)
1002 patron.mailing_address(addr.id());
1007 if(val == null) break;
1008 var resp = new fieldmapper.asvr();
1010 resp.survey(w._survey)
1011 resp.usr(patron.id());
1012 resp.question(w._question)
1014 patron.survey_responses().push(resp);
1018 if(val == null) break;
1020 var map = patron.stat_cat_entries().filter(
1022 return (m.stat_cat() == w._statcat) })[0];
1025 if(map.stat_cat_entry() == val)
1029 map = new fieldmapper.actscecm();
1033 map.stat_cat(w._statcat);
1034 map.stat_cat_entry(val);
1035 map.target_usr(patron.id());
1036 patron.stat_cat_entries().push(map);
1041 patron.ischanged(1);
1042 fieldmapper.standardRequest(
1043 ['open-ils.actor', 'open-ils.actor.patron.update'],
1045 params: [openils.User.authtoken, patron],
1046 oncomplete: function(r) {
1047 newPatron = openils.Util.readResponse(r);
1049 uEditUpdateUserSettings(newPatron.id());
1050 if(stageUser) uEditRemoveStage();
1051 uEditFinishSave(newPatron, doClone);
1058 function uEditRemoveStage() {
1059 var resp = fieldmapper.standardRequest(
1060 ['open-ils.actor', 'open-ils.actor.user.stage.delete'],
1061 { params : [openils.User.authtoken, stageUser.row_id()] }
1065 function uEditFinishSave(newPatron, doClone) {
1067 if(doClone && cloneUser == null)
1068 cloneUser = newPatron.id();
1072 if(xulG && typeof xulG.spawn_editor == 'function' && !patron.isnew() ) {
1073 window.xulG.spawn_editor({ses:openils.User.authtoken,clone:cloneUser});
1077 location.href = location.href.replace(/\?.*/, '') + '?clone=' + cloneUser;
1085 uEditRefreshXUL(newPatron);
1088 function uEditRefresh() {
1089 var usr = cgi.param('usr');
1090 var href = location.href.replace(/\?.*/, '');
1091 href += ((usr) ? '?usr=' + usr : '');
1092 location.href = href;
1095 function uEditRefreshXUL(newuser) {
1096 if (window.xulG && typeof window.xulG.on_save == 'function')
1097 window.xulG.on_save(newuser);
1102 * Create a new address and insert it into the DOM
1103 * @param evt ignored
1104 * @param id The address id
1105 * @param mkLinks If true, set the new address as the
1106 * mailing/billing address for the user
1108 function uEditNewAddr(evt, id, mkLinks) {
1111 id = --uEditAddrVirtId; // new address
1113 var addr = patron.addresses().filter(
1114 function(i) { return (i.id() == id) })[0];
1116 dojo.forEach(addrTemplateRows,
1119 row = tbody.insertBefore(row.cloneNode(true), dojo.byId('new-addr-row'));
1120 row.setAttribute('type', '');
1121 row.setAttribute('addr', id+'');
1123 if(row.getAttribute('fmclass')) {
1124 var widget = fleshFMRow(row, 'aua', {addr:id});
1126 // make new addresses valid by default
1127 if(id < 0 && row.getAttribute('fmfield') == 'valid')
1128 widget.widget.attr('value', true);
1130 } else if(row.getAttribute('name') == 'uedit-addr-pending-row') {
1132 // if it's a pending address, show the 'approve' button
1133 if(addr && openils.Util.isTrue(addr.pending())) {
1134 openils.Util.show(row, 'table-row');
1135 dojo.query('[name=approve-button]', row)[0].onclick =
1136 function() { uEditApproveAddress(addr); };
1138 if(addr.replaces()) {
1139 var div = dojo.query('[name=replaced-addr]', row)[0]
1140 var replaced = patron.addresses().filter(
1141 function(i) { return (i.id() == addr.replaces()) })[0];
1143 div.innerHTML = dojo.string.substitute(localeStrings.REPLACED_ADDRESS, [
1144 replaced.address_type() || '',
1145 replaced.street1() || '',
1146 replaced.street2() || '',
1147 replaced.city() || '',
1148 replaced.state() || '',
1149 replaced.post_code() || ''
1153 openils.Util.hide(dojo.query('[name=replaced-addr-div]', row)[0]);
1157 } else if(row.getAttribute('name') == 'uedit-addr-owner-row') {
1158 // address is owned by someone else. provide option to load the
1159 // user in a different tab
1161 if(addr && addr.usr() != patron.id()) {
1162 openils.Util.show(row, 'table-row');
1163 var link = getByName(row, 'addr-owner');
1165 // fetch the linked user so we can present their name in the UI
1167 if(cloneUserObj && cloneUserObj.id() == addr.usr()) {
1169 cloneUserObj.first_given_name(),
1170 cloneUserObj.second_given_name(),
1171 cloneUserObj.family_name()
1174 addrUser = fieldmapper.standardRequest(
1175 ['open-ils.actor', 'open-ils.actor.user.retrieve.parts'],
1177 openils.User.authtoken,
1179 ['first_given_name', 'second_given_name', 'family_name']
1184 link.innerHTML = (addrUser.map(function(name) { return (name) ? name+' ' : '' })+'').replace(/,/g,''); // TODO i18n
1185 link.onclick = function() {
1186 if(openils.XUL.isXUL()) {
1187 window.xulG.spawn_editor({ses:openils.User.authtoken, usr:addr.usr()})
1189 parent.location.href = location.href.replace(/clone=\d+/, 'usr=' + addr.usr());
1194 } else if(row.getAttribute('name') == 'uedit-addr-divider') {
1195 // link up the billing/mailing address and give the inputs IDs so we can acces the later
1198 var ba = getByName(row, 'billing_address');
1199 ba.id = 'uedit-billing-address-' + id;
1200 if(mkLinks || (patron.billing_address() && patron.billing_address().id() == id))
1204 var ma = getByName(row, 'mailing_address');
1205 ma.id = 'uedit-mailing-address-' + id;
1206 if(mkLinks || (patron.mailing_address() && patron.mailing_address().id() == id))
1210 var btn = dojo.query('[name=delete-button]', row)[0];
1211 if(btn) btn.onclick = function(){ uEditDeleteAddr(id) };
1217 function uEditApproveAddress(addr) {
1218 fieldmapper.standardRequest(
1219 ['open-ils.actor', 'open-ils.actor.user.pending_address.approve'],
1221 params: [openils.User.authtoken, addr],
1223 oncomplete : function(r) {
1224 var oldId = openils.Util.readResponse(r);
1226 // remove addrs from UI
1229 function(addr) { uEditDeleteAddr(addr.id(), true); }
1234 // remove the replaced address
1235 if(oldId != addr.id()) {
1237 patron.addresses().filter(
1238 function(i) { return (i.id() != oldId); }
1243 // fix the the new address
1245 addr.replaces(null);
1258 function uEditDeleteAddr(id, noAlert) {
1260 if(!confirm('Delete address ' + id)) return; /* XXX i18n */
1262 var rows = dojo.query('tr[addr='+id+']', tbody);
1263 for(var i = 0; i < rows.length; i++)
1264 rows[i].parentNode.removeChild(rows[i]);
1265 widgetPile = widgetPile.filter(function(w){return (w._addr != id)});
1268 function uEditToggleRequired() {
1269 if((tbody.className +'').match(/hide-non-required/))
1270 openils.Util.removeCSSClass(tbody, 'hide-non-required');
1272 openils.Util.addCSSClass(tbody, 'hide-non-required');
1273 openils.Util.toggle('uedit-show-required');
1274 openils.Util.toggle('uedit-show-all');
1277 function printable_output() {
1278 var temp; var s = '=-=-=-=\r\n';
1279 for (var idx in widgetPile) {
1280 var w = widgetPile[idx];
1281 var val = uEditWidgetVal(w);
1282 var label = w.idlField.label;
1283 if (temp != w._wtype) {
1287 s += label + ':\t' + (typeof val == 'object' ? '' : val) + '\r\n';
1293 openils.Util.addOnLoad(load);