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