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