]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
plug baseWidgetValue setting for readOnly widgets back in with try/catch and comment...
[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             
342             if(this.readOnly) {
343
344                 /* -------------------------------------------------------------
345                    when using widgets in a grid, the cell may dissapear, which 
346                    kills the underlying DOM node, which causes this to fail.
347                    For now, back out gracefully and let grid getters use
348                    getDisplayString() instead
349                   -------------------------------------------------------------*/
350                 try { 
351                     this.baseWidgetValue(this.getDisplayString());
352                 } catch (E) {};
353
354             } else {
355
356                 this.baseWidgetValue(this.widgetValue);
357                 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence)
358                     this.widget.attr('disabled', true); 
359                 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
360                     this.widget.attr('disabled', true); 
361             }
362             if(this.onload)
363                 this.onload(this.widget, this);
364         },
365
366         _buildOrgSelector : function() {
367             dojo.require('fieldmapper.OrgUtils');
368             dojo.require('openils.widget.FilteringTreeSelect');
369             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
370             this.widget.searchAttr = 'shortname';
371             this.widget.labelAttr = 'shortname';
372             this.widget.parentField = 'parent_ou';
373             var user = new openils.User();
374
375             if(this.widgetValue == null) 
376                 this.widgetValue = user.user.ws_ou();
377             
378             // if we have a limit perm, find the relevent orgs (async)
379             if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
380                 this.async = true;
381                 var self = this;
382                 user.getPermOrgList(this.orgLimitPerms, 
383                     function(orgList) {
384                         self.widget.tree = orgList;
385                         self.widget.startup();
386                         self._widgetLoaded();
387                     }
388                 );
389
390             } else {
391                 this.widget.tree = fieldmapper.aou.globalOrgTree;
392                 this.widget.startup();
393             }
394         },
395
396         _buildPermGrpSelector : function() {
397             dojo.require('openils.widget.FilteringTreeSelect');
398             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
399             this.widget.searchAttr = 'name';
400
401             if(this.cache.permGrpTree) {
402                 this.widget.tree = this.cache.permGrpTree;
403                 this.widget.startup();
404                 return true;
405             } 
406
407             var self = this;
408             this.async = true;
409             new openils.PermaCrud().retrieveAll('pgt', {
410                 async : !this.forceSync,
411                 oncomplete : function(r) {
412                     var list = openils.Util.readResponse(r, false, true);
413                     if(!list) return;
414                     var map = {};
415                     var root = null;
416                     for(var l in list)
417                         map[list[l].id()] = list[l];
418                     for(var l in list) {
419                         var node = list[l];
420                         var pnode = map[node.parent()];
421                         if(!pnode) {root = node; continue;}
422                         if(!pnode.children()) pnode.children([]);
423                         pnode.children().push(node);
424                     }
425                     self.widget.tree = self.cache.permGrpTree = root;
426                     self.widget.startup();
427                     self._widgetLoaded();
428                 }
429             });
430
431             return true;
432         },
433
434         _buildCopyLocSelector : function() {
435             dojo.require('dijit.form.FilteringSelect');
436             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
437             this.widget.searchAttr = this.widget.labalAttr = 'name';
438             this.widget.valueAttr = 'id';
439
440             if(this.cache.copyLocStore) {
441                 this.widget.store = this.cache.copyLocStore;
442                 this.widget.startup();
443                 this.async = false;
444                 return true;
445             } 
446
447             // my orgs
448             var ws_ou = openils.User.user.ws_ou();
449             var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
450             orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
451
452             var self = this;
453             new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
454                 async : !this.forceSync,
455                 oncomplete : function(r) {
456                     var list = openils.Util.readResponse(r, false, true);
457                     if(!list) return;
458                     self.widget.store = 
459                         new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
460                     self.cache.copyLocStore = self.widget.store;
461                     self.widget.startup();
462                     self._widgetLoaded();
463                 }
464             });
465
466             return true;
467         }
468     });
469
470     openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
471     openils.widget.AutoFieldWidget.cache = {};
472 }
473