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