]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
when getDisplayString() is called without ever having built a true widget, the code...
[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 = null;
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 't': 
117                         case 'true': 
118                             return openils.widget.AutoFieldWidget.localeStrings.TRUE; 
119                         case 'f' : 
120                         case 'false' : 
121                             return openils.widget.AutoFieldWidget.localeStrings.FALSE;
122                         case 'unset' : return openils.widget.AutoFieldWidget.localeStrings.UNSET;
123                         case true : return openils.widget.AutoFieldWidget.localeStrings.TRUE; 
124                         default: return openils.widget.AutoFieldWidget.localeStrings.FALSE;
125                     }
126                 case 'timestamp':
127                     if (!value) return '';
128                     dojo.require('dojo.date.locale');
129                     dojo.require('dojo.date.stamp');
130                     var date = dojo.date.stamp.fromISOString(value);
131                     return dojo.date.locale.format(date, {formatLength:'short'});
132                 case 'org_unit':
133                     if(value === null || value === undefined) return '';
134                     return fieldmapper.aou.findOrgUnit(value).shortname();
135                 case 'int':
136                 case 'float':
137                     if(isNaN(value)) value = 0;
138                 default:
139                     if(value === undefined || value === null)
140                         value = '';
141                     return value+'';
142             }
143         },
144
145         build : function(onload) {
146
147             if(this.widgetValue == null)
148                 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
149
150             if(this.widget) {
151                 // core widget provided for us, attach and move on
152                 if(this.parentNode) // may already be in the "right" place
153                     this.parentNode.appendChild(this.widget.domNode);
154                 if(this.widget.attr('value') == null)
155                     this._widgetLoaded();
156                 return;
157             }
158             
159             if(!this.parentNode) // give it somewhere to live so that dojo won't complain
160                 this.parentNode = dojo.create('div');
161
162             this.onload = onload;
163
164             if(this.readOnly) {
165                 dojo.require('dijit.layout.ContentPane');
166                 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
167                 if(this.widgetValue !== null)
168                     this._tryLinkedDisplayField();
169
170             } else if(this.widgetClass) {
171                 dojo.require(this.widgetClass);
172                 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
173
174             } else {
175
176                 switch(this.idlField.datatype) {
177                     
178                     case 'id':
179                         dojo.require('dijit.form.TextBox');
180                         this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
181                         break;
182
183                     case 'org_unit':
184                         this._buildOrgSelector();
185                         break;
186
187                     case 'money':
188                         dojo.require('dijit.form.CurrencyTextBox');
189                         this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
190                         break;
191
192                     case 'int':
193                         dojo.require('dijit.form.NumberTextBox');
194                         this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
195                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
196                         break;
197
198                     case 'float':
199                         dojo.require('dijit.form.NumberTextBox');
200                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
201                         break;
202
203                     case 'timestamp':
204                         dojo.require('dijit.form.DateTextBox');
205                         dojo.require('dojo.date.stamp');
206                         this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
207                         if(this.widgetValue != null) 
208                             this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
209                         break;
210
211                     case 'bool':
212                         if(this.ternary) {
213                             dojo.require('dijit.form.FilteringSelect');
214                             var store = new dojo.data.ItemFileReadStore({
215                                 data:{
216                                     identifier : 'value',
217                                     items:[
218                                         {label : openils.widget.AutoFieldWidget.localeStrings.UNSET, value : 'unset'},
219                                         {label : openils.widget.AutoFieldWidget.localeStrings.TRUE, value : 'true'},
220                                         {label : openils.widget.AutoFieldWidget.localeStrings.FALSE, value : 'false'}
221                                     ]
222                                 }
223                             });
224                             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
225                             this.widget.searchAttr = this.widget.labelAttr = 'label';
226                             this.widget.valueAttr = 'value';
227                             this.widget.store = store;
228                             this.widget.startup();
229                             this.widgetValue = (this.widgetValue === null) ? 'unset' : 
230                                 (openils.Util.isTrue(this.widgetValue)) ? 'true' : 'false';
231                         } else {
232                             dojo.require('dijit.form.CheckBox');
233                             this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
234                             this.widgetValue = openils.Util.isTrue(this.widgetValue);
235                         }
236                         break;
237
238                     case 'link':
239                         if(this._buildLinkSelector()) break;
240
241                     default:
242                         if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
243                             dojo.require('dijit.form.ValidationTextBox');
244                             this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
245                         } else {
246                             dojo.require('dijit.form.TextBox');
247                             this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
248                         }
249                 }
250             }
251
252             if(!this.async) this._widgetLoaded();
253             return this.widget;
254         },
255
256         // we want to display the value for our widget.  However, instead of displaying
257         // an ID, for exmaple, display the value for the 'selector' field on the object
258         // the ID points to
259         _tryLinkedDisplayField : function(noAsync) {
260
261             if(this.idlField.datatype == 'org_unit')
262                 return false; // we already handle org_units, no need to re-fetch
263
264             // user opted to bypass fetching this linked data
265             if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
266                 return false;
267
268             var linkInfo = this._getLinkSelector();
269             if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector)) 
270                 return false;
271             var lclass = linkInfo.linkClass;
272
273             if(lclass == 'aou') {
274                 this.widgetValue = fieldmapper.aou.findOrgUnit(this.widgetValue).shortname();
275                 return;
276             }
277
278             // first try the store cache
279             var self = this;
280             if(this.cache[this.auth].list[lclass]) {
281                 var store = this.cache[this.auth].list[lclass];
282                 var query = {};
283                 query[linkInfo.vfield.name] = ''+this.widgetValue;
284                 store.fetch({query:query, onComplete:
285                     function(list) {
286                         self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
287                     }
288                 });
289                 return;
290             }
291
292             // then try the single object cache
293             if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
294                 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
295                 return;
296             }
297
298             console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
299
300             // if those fail, fetch the linked object
301             this.async = true;
302             var self = this;
303             new openils.PermaCrud().retrieve(lclass, this.widgetValue, {   
304                 async : !this.forceSync,
305                 oncomplete : function(r) {
306                     var item = openils.Util.readResponse(r);
307                     var newvalue = item[linkInfo.vfield.selector]();
308
309                     if(!self.cache[self.auth].single[lclass])
310                         self.cache[self.auth].single[lclass] = {};
311                     self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
312
313                     self.widgetValue = newvalue;
314                     self.widget.startup();
315                     self._widgetLoaded();
316                 }
317             });
318         },
319
320         _getLinkSelector : function() {
321             var linkClass = this.idlField['class'];
322             if(this.idlField.reltype != 'has_a')  return false;
323             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
324             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
325
326             var vfield;
327             var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
328
329             for(var f in rclassIdl.fields) {
330                 if(this.idlField.key == rclassIdl.fields[f].name) {
331                     vfield = rclassIdl.fields[f];
332                     break;
333                 }
334             }
335
336             if(!vfield) 
337                 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
338
339             return {
340                 linkClass : linkClass,
341                 vfield : vfield
342             };
343         },
344
345         _buildLinkSelector : function() {
346             var selectorInfo = this._getLinkSelector();
347             if(!selectorInfo) return false;
348
349             var linkClass = selectorInfo.linkClass;
350             var vfield = selectorInfo.vfield;
351
352             this.async = true;
353
354             if(linkClass == 'pgt')
355                 return this._buildPermGrpSelector();
356             if(linkClass == 'aou')
357                 return this._buildOrgSelector();
358             if(linkClass == 'acpl')
359                 return this._buildCopyLocSelector();
360
361
362             dojo.require('dojo.data.ItemFileReadStore');
363             dojo.require('dijit.form.FilteringSelect');
364
365             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
366             this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
367             this.widget.valueAttr = vfield.name;
368
369             var self = this;
370             var oncomplete = function(list) {
371                 if(list) {
372                     self.widget.store = 
373                         new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
374                     self.cache[self.auth].list[linkClass] = self.widget.store;
375                 } else {
376                     self.widget.store = self.cache[self.auth].list[linkClass];
377                 }
378                 self.widget.startup();
379                 self._widgetLoaded();
380             };
381
382             if(this.cache[self.auth].list[linkClass]) {
383                 oncomplete();
384
385             } else {
386                 new openils.PermaCrud().retrieveAll(linkClass, {   
387                     async : !this.forceSync,
388                     oncomplete : function(r) {
389                         var list = openils.Util.readResponse(r, false, true);
390                         oncomplete(list);
391                     }
392                 });
393             }
394
395             return true;
396         },
397
398         /**
399          * For widgets that run asynchronously, provide a callback for finishing up
400          */
401         _widgetLoaded : function(value) {
402             
403             if(this.readOnly) {
404
405                 /* -------------------------------------------------------------
406                    when using widgets in a grid, the cell may dissapear, which 
407                    kills the underlying DOM node, which causes this to fail.
408                    For now, back out gracefully and let grid getters use
409                    getDisplayString() instead
410                   -------------------------------------------------------------*/
411                 try { 
412                     this.baseWidgetValue(this.getDisplayString());
413                 } catch (E) {};
414
415             } else {
416
417                 this.baseWidgetValue(this.widgetValue);
418                 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
419                     this.widget.attr('disabled', true); 
420                 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
421                     this.widget.attr('disabled', true); 
422             }
423             if(this.onload)
424                 this.onload(this.widget, this);
425         },
426
427         _buildOrgSelector : function() {
428             dojo.require('fieldmapper.OrgUtils');
429             dojo.require('openils.widget.FilteringTreeSelect');
430             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
431             this.widget.searchAttr = 'shortname';
432             this.widget.labelAttr = 'shortname';
433             this.widget.parentField = 'parent_ou';
434             var user = new openils.User();
435
436             if(this.widgetValue == null) 
437                 this.widgetValue = user.user.ws_ou();
438             
439             // if we have a limit perm, find the relevent orgs (async)
440             if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
441                 this.async = true;
442                 var self = this;
443                 user.getPermOrgList(this.orgLimitPerms, 
444                     function(orgList) {
445                         self.widget.tree = orgList;
446                         self.widget.startup();
447                         self._widgetLoaded();
448                     }
449                 );
450
451             } else {
452                 this.widget.tree = fieldmapper.aou.globalOrgTree;
453                 this.widget.startup();
454             }
455         },
456
457         _buildPermGrpSelector : function() {
458             dojo.require('openils.widget.FilteringTreeSelect');
459             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
460             this.widget.searchAttr = 'name';
461
462             if(this.cache.permGrpTree) {
463                 this.widget.tree = this.cache.permGrpTree;
464                 this.widget.startup();
465                 return true;
466             } 
467
468             var self = this;
469             this.async = true;
470             new openils.PermaCrud().retrieveAll('pgt', {
471                 async : !this.forceSync,
472                 oncomplete : function(r) {
473                     var list = openils.Util.readResponse(r, false, true);
474                     if(!list) return;
475                     var map = {};
476                     var root = null;
477                     for(var l in list)
478                         map[list[l].id()] = list[l];
479                     for(var l in list) {
480                         var node = list[l];
481                         var pnode = map[node.parent()];
482                         if(!pnode) {root = node; continue;}
483                         if(!pnode.children()) pnode.children([]);
484                         pnode.children().push(node);
485                     }
486                     self.widget.tree = self.cache.permGrpTree = root;
487                     self.widget.startup();
488                     self._widgetLoaded();
489                 }
490             });
491
492             return true;
493         },
494
495         _buildCopyLocSelector : function() {
496             dojo.require('dijit.form.FilteringSelect');
497             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
498             this.widget.searchAttr = this.widget.labalAttr = 'name';
499             this.widget.valueAttr = 'id';
500
501             if(this.cache.copyLocStore) {
502                 this.widget.store = this.cache.copyLocStore;
503                 this.widget.startup();
504                 this.async = false;
505                 return true;
506             } 
507
508             // my orgs
509             var ws_ou = openils.User.user.ws_ou();
510             var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
511             orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
512
513             var self = this;
514             new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
515                 async : !this.forceSync,
516                 oncomplete : function(r) {
517                     var list = openils.Util.readResponse(r, false, true);
518                     if(!list) return;
519                     self.widget.store = 
520                         new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
521                     self.cache.copyLocStore = self.widget.store;
522                     self.widget.startup();
523                     self._widgetLoaded();
524                 }
525             });
526
527             return true;
528         }
529     });
530
531     openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
532     openils.widget.AutoFieldWidget.cache = {};
533 }
534