]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
Checkboxes in the patron editor are getting a value of 'on' (a change in dojo?)....
[working/Evergreen.git] / Open-ILS / web / js / dojo / openils / widget / AutoFieldWidget.js
1 if(!dojo._hasResource['openils.widget.AutoFieldWidget']) {
2     dojo.provide('openils.widget.AutoFieldWidget');
3     dojo.require('openils.Util');
4     dojo.require('openils.User');
5     dojo.require('fieldmapper.IDL');
6     dojo.require('openils.PermaCrud');
7         dojo.requireLocalization("openils.widget", "AutoFieldWidget");
8
9     dojo.declare('openils.widget.AutoFieldWidget', null, {
10
11         async : false,
12
13         /**
14          * args:
15          *  idlField -- Field description object from fieldmapper.IDL.fmclasses
16          *  fmObject -- If available, the object being edited.  This will be used 
17          *      to set the value of the widget.
18          *  fmClass -- Class name (not required if idlField or fmObject is set)
19          *  fmField -- Field name (not required if idlField)
20          *  parentNode -- If defined, the widget will be appended to this DOM node
21          *  dijitArgs -- Optional parameters object, passed directly to the dojo widget
22          *  orgLimitPerms -- If this field defines a set of org units and an orgLimitPerms 
23          *      is defined, the code will limit the org units in the set to those
24          *      allowed by the permission
25          *  selfReference -- The primary purpose of an AutoFieldWidget is to render the value
26          *      or widget for a field on an object (that may or may not link to another object).
27          *      selfReference allows you to sidestep the indirection and create a selector widget
28          *      based purely on an fmClass.  To get a dropdown of all of the 'abc'
29          *      objects, pass in {selfReference : true, fmClass : 'abc'}.  
30          */
31         constructor : function(args) {
32             for(var k in args)
33                 this[k] = args[k];
34
35             // find the field description in the IDL if not provided
36             if(this.fmObject) 
37                 this.fmClass = this.fmObject.classname;
38             this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
39             this.suppressLinkedFields = args.suppressLinkedFields || [];
40
41             if(this.selfReference) {
42                 this.fmField = fieldmapper.IDL.fmclasses[this.fmClass].pkey;
43                 
44                 // create a mock-up of the idlField object.  
45                 this.idlField = {
46                     datatype : 'link',
47                     'class' : this.fmClass,
48                     reltype : 'has_a',
49                     key : this.fmField,
50                     name : this.fmField
51                 };
52
53             } else {
54
55                 if(!this.idlField) {
56                     this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
57                     var fields = this.fmIDL.fields;
58                     for(var f in fields) 
59                         if(fields[f].name == this.fmField)
60                             this.idlField = fields[f];
61                 }
62             }
63
64             if(!this.idlField) 
65                 throw new Error("AutoFieldWidget could not determine which field to render.  We need more information. fmClass=" + 
66                     this.fmClass + ' fmField=' + this.fmField + ' fmObject=' + js2JSON(this.fmObject));
67
68             this.auth = openils.User.authtoken;
69             this.cache = openils.widget.AutoFieldWidget.cache;
70             this.cache[this.auth] = this.cache[this.auth] || {};
71             this.cache[this.auth].single = this.cache[this.auth].single || {};
72             this.cache[this.auth].list = this.cache[this.auth].list || {};
73         },
74
75         /**
76          * Turn the widget-stored value into a value oils understands
77          */
78         getFormattedValue : function() {
79             var value = this.baseWidgetValue();
80             switch(this.idlField.datatype) {
81                 case 'bool':
82                     switch(value) {
83                         case 'true': return 't';
84                         case 'on': return 't';
85                         case 'false' : return 'f';
86                         case 'unset' : return null;
87                         case true : return 't';
88                         default: return 'f';
89                     }
90                 case 'timestamp':
91                     if(!value) return null;
92                     return dojo.date.stamp.toISOString(value);
93                 case 'int':
94                 case 'float':
95                 case 'money':
96                     if(isNaN(value)) value = 0;
97                 default:
98                     return (value === '') ? null : value;
99             }
100         },
101
102         baseWidgetValue : function(value) {
103             var attr = (this.readOnly) ? 'content' : 'value';
104             if(arguments.length) this.widget.attr(attr, value);
105             return this.widget.attr(attr);
106         },
107         
108         /**
109          * Turn the widget-stored value into something visually suitable
110          */
111         getDisplayString : function() {
112             var value = this.widgetValue;
113             switch(this.idlField.datatype) {
114                 case 'bool':
115                     switch(value) {
116                         case 'true': return openils.widget.AutoFieldWidget.localeStrings.TRUE; 
117                         case 'false' : return openils.widget.AutoFieldWidget.localeStrings.FALSE;
118                         case 'unset' : return openils.widget.AutoFieldWidget.localeStrings.UNSET;
119                         case true : return openils.widget.AutoFieldWidget.localeStrings.TRUE; 
120                         default: return openils.widget.AutoFieldWidget.localeStrings.FALSE;
121                     }
122                 case 'timestamp':
123                     dojo.require('dojo.date.locale');
124                     dojo.require('dojo.date.stamp');
125                     var date = dojo.date.stamp.fromISOString(value);
126                     return dojo.date.locale.format(date, {formatLength:'short'});
127                 case 'org_unit':
128                     if(value === null || value === undefined) return '';
129                     return fieldmapper.aou.findOrgUnit(value).shortname();
130                 case 'int':
131                 case 'float':
132                     if(isNaN(value)) value = 0;
133                 default:
134                     if(value === undefined || value === null)
135                         value = '';
136                     return value+'';
137             }
138         },
139
140         build : function(onload) {
141
142             if(this.widgetValue == null)
143                 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
144
145             if(this.widget) {
146                 // core widget provided for us, attach and move on
147                 if(this.parentNode) // may already be in the "right" place
148                     this.parentNode.appendChild(this.widget.domNode);
149                 this._widgetLoaded();
150                 return;
151             }
152             
153             if(!this.parentNode) // give it somewhere to live so that dojo won't complain
154                 this.parentNode = dojo.create('div');
155
156             this.onload = onload;
157
158             if(this.readOnly) {
159                 dojo.require('dijit.layout.ContentPane');
160                 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
161                 if(this.widgetValue !== null)
162                     this._tryLinkedDisplayField();
163
164             } else if(this.widgetClass) {
165                 dojo.require(this.widgetClass);
166                 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
167
168             } else {
169
170                 switch(this.idlField.datatype) {
171                     
172                     case 'id':
173                         dojo.require('dijit.form.TextBox');
174                         this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
175                         break;
176
177                     case 'org_unit':
178                         this._buildOrgSelector();
179                         break;
180
181                     case 'money':
182                         dojo.require('dijit.form.CurrencyTextBox');
183                         this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
184                         break;
185
186                     case 'int':
187                         dojo.require('dijit.form.NumberTextBox');
188                         this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
189                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
190                         break;
191
192                     case 'float':
193                         dojo.require('dijit.form.NumberTextBox');
194                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
195                         break;
196
197                     case 'timestamp':
198                         dojo.require('dijit.form.DateTextBox');
199                         dojo.require('dojo.date.stamp');
200                         this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
201                         if(this.widgetValue != null) 
202                             this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
203                         break;
204
205                     case 'bool':
206                         if(this.ternary) {
207                             dojo.require('dijit.form.FilteringSelect');
208                             var store = new dojo.data.ItemFileReadStore({
209                                 data:{
210                                     identifier : 'value',
211                                     items:[
212                                         {label : openils.widget.AutoFieldWidget.localeStrings.UNSET, value : 'unset'},
213                                         {label : openils.widget.AutoFieldWidget.localeStrings.TRUE, value : 'true'},
214                                         {label : openils.widget.AutoFieldWidget.localeStrings.FALSE, value : 'false'}
215                                     ]
216                                 }
217                             });
218                             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
219                             this.widget.searchAttr = this.widget.labelAttr = 'label';
220                             this.widget.valueAttr = 'value';
221                             this.widget.store = store;
222                             this.widget.startup();
223                             this.widgetValue = (this.widgetValue === null) ? 'unset' : 
224                                 (openils.Util.isTrue(this.widgetValue)) ? 'true' : 'false';
225                         } else {
226                             dojo.require('dijit.form.CheckBox');
227                             this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
228                             this.widgetValue = openils.Util.isTrue(this.widgetValue);
229                         }
230                         break;
231
232                     case 'link':
233                         if(this._buildLinkSelector()) break;
234
235                     default:
236                         if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
237                             dojo.require('dijit.form.ValidationTextBox');
238                             this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
239                         } else {
240                             dojo.require('dijit.form.TextBox');
241                             this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
242                         }
243                 }
244             }
245
246             if(!this.async) this._widgetLoaded();
247             return this.widget;
248         },
249
250         // we want to display the value for our widget.  However, instead of displaying
251         // an ID, for exmaple, display the value for the 'selector' field on the object
252         // the ID points to
253         _tryLinkedDisplayField : function(noAsync) {
254
255             if(this.idlField.datatype == 'org_unit')
256                 return false; // we already handle org_units, no need to re-fetch
257
258             // user opted to bypass fetching this linked data
259             if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
260                 return false;
261
262             var linkInfo = this._getLinkSelector();
263             if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector)) 
264                 return false;
265             var lclass = linkInfo.linkClass;
266
267             if(lclass == 'aou') {
268                 this.widgetValue = fieldmapper.aou.findOrgUnit(this.widgetValue).shortname();
269                 return;
270             }
271
272             // first try the store cache
273             var self = this;
274             if(this.cache[this.auth].list[lclass]) {
275                 var store = this.cache[this.auth].list[lclass];
276                 var query = {};
277                 query[linkInfo.vfield.name] = ''+this.widgetValue;
278                 store.fetch({query:query, onComplete:
279                     function(list) {
280                         self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
281                     }
282                 });
283                 return;
284             }
285
286             // then try the single object cache
287             if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
288                 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
289                 return;
290             }
291
292             console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
293
294             // if those fail, fetch the linked object
295             this.async = true;
296             var self = this;
297             new openils.PermaCrud().retrieve(lclass, this.widgetValue, {   
298                 async : !this.forceSync,
299                 oncomplete : function(r) {
300                     var item = openils.Util.readResponse(r);
301                     var newvalue = item[linkInfo.vfield.selector]();
302
303                     if(!self.cache[self.auth].single[lclass])
304                         self.cache[self.auth].single[lclass] = {};
305                     self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
306
307                     self.widgetValue = newvalue;
308                     self.widget.startup();
309                     self._widgetLoaded();
310                 }
311             });
312         },
313
314         _getLinkSelector : function() {
315             var linkClass = this.idlField['class'];
316             if(this.idlField.reltype != 'has_a')  return false;
317             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
318             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
319
320             var vfield;
321             var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
322
323             for(var f in rclassIdl.fields) {
324                 if(this.idlField.key == rclassIdl.fields[f].name) {
325                     vfield = rclassIdl.fields[f];
326                     break;
327                 }
328             }
329
330             if(!vfield) 
331                 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
332
333             return {
334                 linkClass : linkClass,
335                 vfield : vfield
336             };
337         },
338
339         _buildLinkSelector : function() {
340             var selectorInfo = this._getLinkSelector();
341             if(!selectorInfo) return false;
342
343             var linkClass = selectorInfo.linkClass;
344             var vfield = selectorInfo.vfield;
345
346             this.async = true;
347
348             if(linkClass == 'pgt')
349                 return this._buildPermGrpSelector();
350             if(linkClass == 'aou')
351                 return this._buildOrgSelector();
352             if(linkClass == 'acpl')
353                 return this._buildCopyLocSelector();
354
355
356             dojo.require('dojo.data.ItemFileReadStore');
357             dojo.require('dijit.form.FilteringSelect');
358
359             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
360             this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
361             this.widget.valueAttr = vfield.name;
362
363             var self = this;
364             var oncomplete = function(list) {
365                 if(list) {
366                     self.widget.store = 
367                         new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
368                     self.cache[self.auth].list[linkClass] = self.widget.store;
369                 } else {
370                     self.widget.store = self.cache[self.auth].list[linkClass];
371                 }
372                 self.widget.startup();
373                 self._widgetLoaded();
374             };
375
376             if(this.cache[self.auth].list[linkClass]) {
377                 oncomplete();
378
379             } else {
380                 new openils.PermaCrud().retrieveAll(linkClass, {   
381                     async : !this.forceSync,
382                     oncomplete : function(r) {
383                         var list = openils.Util.readResponse(r, false, true);
384                         oncomplete(list);
385                     }
386                 });
387             }
388
389             return true;
390         },
391
392         /**
393          * For widgets that run asynchronously, provide a callback for finishing up
394          */
395         _widgetLoaded : function(value) {
396             
397             if(this.readOnly) {
398
399                 /* -------------------------------------------------------------
400                    when using widgets in a grid, the cell may dissapear, which 
401                    kills the underlying DOM node, which causes this to fail.
402                    For now, back out gracefully and let grid getters use
403                    getDisplayString() instead
404                   -------------------------------------------------------------*/
405                 try { 
406                     this.baseWidgetValue(this.getDisplayString());
407                 } catch (E) {};
408
409             } else {
410
411                 this.baseWidgetValue(this.widgetValue);
412                 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
413                     this.widget.attr('disabled', true); 
414                 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
415                     this.widget.attr('disabled', true); 
416             }
417             if(this.onload)
418                 this.onload(this.widget, this);
419         },
420
421         _buildOrgSelector : function() {
422             dojo.require('fieldmapper.OrgUtils');
423             dojo.require('openils.widget.FilteringTreeSelect');
424             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
425             this.widget.searchAttr = 'shortname';
426             this.widget.labelAttr = 'shortname';
427             this.widget.parentField = 'parent_ou';
428             var user = new openils.User();
429
430             if(this.widgetValue == null) 
431                 this.widgetValue = user.user.ws_ou();
432             
433             // if we have a limit perm, find the relevent orgs (async)
434             if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
435                 this.async = true;
436                 var self = this;
437                 user.getPermOrgList(this.orgLimitPerms, 
438                     function(orgList) {
439                         self.widget.tree = orgList;
440                         self.widget.startup();
441                         self._widgetLoaded();
442                     }
443                 );
444
445             } else {
446                 this.widget.tree = fieldmapper.aou.globalOrgTree;
447                 this.widget.startup();
448             }
449         },
450
451         _buildPermGrpSelector : function() {
452             dojo.require('openils.widget.FilteringTreeSelect');
453             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
454             this.widget.searchAttr = 'name';
455
456             if(this.cache.permGrpTree) {
457                 this.widget.tree = this.cache.permGrpTree;
458                 this.widget.startup();
459                 return true;
460             } 
461
462             var self = this;
463             this.async = true;
464             new openils.PermaCrud().retrieveAll('pgt', {
465                 async : !this.forceSync,
466                 oncomplete : function(r) {
467                     var list = openils.Util.readResponse(r, false, true);
468                     if(!list) return;
469                     var map = {};
470                     var root = null;
471                     for(var l in list)
472                         map[list[l].id()] = list[l];
473                     for(var l in list) {
474                         var node = list[l];
475                         var pnode = map[node.parent()];
476                         if(!pnode) {root = node; continue;}
477                         if(!pnode.children()) pnode.children([]);
478                         pnode.children().push(node);
479                     }
480                     self.widget.tree = self.cache.permGrpTree = root;
481                     self.widget.startup();
482                     self._widgetLoaded();
483                 }
484             });
485
486             return true;
487         },
488
489         _buildCopyLocSelector : function() {
490             dojo.require('dijit.form.FilteringSelect');
491             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
492             this.widget.searchAttr = this.widget.labalAttr = 'name';
493             this.widget.valueAttr = 'id';
494
495             if(this.cache.copyLocStore) {
496                 this.widget.store = this.cache.copyLocStore;
497                 this.widget.startup();
498                 this.async = false;
499                 return true;
500             } 
501
502             // my orgs
503             var ws_ou = openils.User.user.ws_ou();
504             var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
505             orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
506
507             var self = this;
508             new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
509                 async : !this.forceSync,
510                 oncomplete : function(r) {
511                     var list = openils.Util.readResponse(r, false, true);
512                     if(!list) return;
513                     self.widget.store = 
514                         new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
515                     self.cache.copyLocStore = self.widget.store;
516                     self.widget.startup();
517                     self._widgetLoaded();
518                 }
519             });
520
521             return true;
522         }
523     });
524
525     openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
526     openils.widget.AutoFieldWidget.cache = {};
527 }
528