]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
if a timestamp is null, don't attempt to turn it into an iso string
[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                     if(!value) return null;
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 = dojo.create('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].list[lclass]) {
221                 var store = this.cache[this.auth].list[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.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
234                 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
235                 return;
236             }
237
238             console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
239
240             // if those fail, fetch the linked object
241             this.async = true;
242             var self = this;
243             new openils.PermaCrud().retrieve(lclass, this.widgetValue, {   
244                 async : !this.forceSync,
245                 oncomplete : function(r) {
246                     var item = openils.Util.readResponse(r);
247                     var newvalue = item[linkInfo.vfield.selector]();
248
249                     if(!self.cache[self.auth].single[lclass])
250                         self.cache[self.auth].single[lclass] = {};
251                     self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
252
253                     self.widgetValue = newvalue;
254                     self.widget.startup();
255                     self._widgetLoaded();
256                 }
257             });
258         },
259
260         _getLinkSelector : function() {
261             var linkClass = this.idlField['class'];
262             if(this.idlField.reltype != 'has_a')  return false;
263             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
264             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
265
266             var vfield;
267             var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
268
269             for(var f in rclassIdl.fields) {
270                 if(this.idlField.key == rclassIdl.fields[f].name) {
271                     vfield = rclassIdl.fields[f];
272                     break;
273                 }
274             }
275
276             if(!vfield) 
277                 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
278
279             return {
280                 linkClass : linkClass,
281                 vfield : vfield
282             };
283         },
284
285         _buildLinkSelector : function() {
286             var selectorInfo = this._getLinkSelector();
287             if(!selectorInfo) return false;
288
289             var linkClass = selectorInfo.linkClass;
290             var vfield = selectorInfo.vfield;
291
292             this.async = true;
293
294             if(linkClass == 'pgt')
295                 return this._buildPermGrpSelector();
296             if(linkClass == 'aou')
297                 return this._buildOrgSelector();
298             if(linkClass == 'acpl')
299                 return this._buildCopyLocSelector();
300
301
302             dojo.require('dojo.data.ItemFileReadStore');
303             dojo.require('dijit.form.FilteringSelect');
304
305             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
306             this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
307             this.widget.valueAttr = vfield.name;
308
309             var self = this;
310             var oncomplete = function(list) {
311                 if(list) {
312                     self.widget.store = 
313                         new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
314                     self.cache[self.auth].list[linkClass] = self.widget.store;
315                 } else {
316                     self.widget.store = self.cache[self.auth].list[linkClass];
317                 }
318                 self.widget.startup();
319                 self._widgetLoaded();
320             };
321
322             if(this.cache[self.auth].list[linkClass]) {
323                 oncomplete();
324
325             } else {
326                 new openils.PermaCrud().retrieveAll(linkClass, {   
327                     async : !this.forceSync,
328                     oncomplete : function(r) {
329                         var list = openils.Util.readResponse(r, false, true);
330                         oncomplete(list);
331                     }
332                 });
333             }
334
335             return true;
336         },
337
338         /**
339          * For widgets that run asynchronously, provide a callback for finishing up
340          */
341         _widgetLoaded : function(value) {
342             
343             if(this.readOnly) {
344
345                 /* -------------------------------------------------------------
346                    when using widgets in a grid, the cell may dissapear, which 
347                    kills the underlying DOM node, which causes this to fail.
348                    For now, back out gracefully and let grid getters use
349                    getDisplayString() instead
350                   -------------------------------------------------------------*/
351                 try { 
352                     this.baseWidgetValue(this.getDisplayString());
353                 } catch (E) {};
354
355             } else {
356
357                 this.baseWidgetValue(this.widgetValue);
358                 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence)
359                     this.widget.attr('disabled', true); 
360                 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
361                     this.widget.attr('disabled', true); 
362             }
363             if(this.onload)
364                 this.onload(this.widget, this);
365         },
366
367         _buildOrgSelector : function() {
368             dojo.require('fieldmapper.OrgUtils');
369             dojo.require('openils.widget.FilteringTreeSelect');
370             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
371             this.widget.searchAttr = 'shortname';
372             this.widget.labelAttr = 'shortname';
373             this.widget.parentField = 'parent_ou';
374             var user = new openils.User();
375
376             if(this.widgetValue == null) 
377                 this.widgetValue = user.user.ws_ou();
378             
379             // if we have a limit perm, find the relevent orgs (async)
380             if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
381                 this.async = true;
382                 var self = this;
383                 user.getPermOrgList(this.orgLimitPerms, 
384                     function(orgList) {
385                         self.widget.tree = orgList;
386                         self.widget.startup();
387                         self._widgetLoaded();
388                     }
389                 );
390
391             } else {
392                 this.widget.tree = fieldmapper.aou.globalOrgTree;
393                 this.widget.startup();
394             }
395         },
396
397         _buildPermGrpSelector : function() {
398             dojo.require('openils.widget.FilteringTreeSelect');
399             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
400             this.widget.searchAttr = 'name';
401
402             if(this.cache.permGrpTree) {
403                 this.widget.tree = this.cache.permGrpTree;
404                 this.widget.startup();
405                 return true;
406             } 
407
408             var self = this;
409             this.async = true;
410             new openils.PermaCrud().retrieveAll('pgt', {
411                 async : !this.forceSync,
412                 oncomplete : function(r) {
413                     var list = openils.Util.readResponse(r, false, true);
414                     if(!list) return;
415                     var map = {};
416                     var root = null;
417                     for(var l in list)
418                         map[list[l].id()] = list[l];
419                     for(var l in list) {
420                         var node = list[l];
421                         var pnode = map[node.parent()];
422                         if(!pnode) {root = node; continue;}
423                         if(!pnode.children()) pnode.children([]);
424                         pnode.children().push(node);
425                     }
426                     self.widget.tree = self.cache.permGrpTree = root;
427                     self.widget.startup();
428                     self._widgetLoaded();
429                 }
430             });
431
432             return true;
433         },
434
435         _buildCopyLocSelector : function() {
436             dojo.require('dijit.form.FilteringSelect');
437             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
438             this.widget.searchAttr = this.widget.labalAttr = 'name';
439             this.widget.valueAttr = 'id';
440
441             if(this.cache.copyLocStore) {
442                 this.widget.store = this.cache.copyLocStore;
443                 this.widget.startup();
444                 this.async = false;
445                 return true;
446             } 
447
448             // my orgs
449             var ws_ou = openils.User.user.ws_ou();
450             var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
451             orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
452
453             var self = this;
454             new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
455                 async : !this.forceSync,
456                 oncomplete : function(r) {
457                     var list = openils.Util.readResponse(r, false, true);
458                     if(!list) return;
459                     self.widget.store = 
460                         new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
461                     self.cache.copyLocStore = self.widget.store;
462                     self.widget.startup();
463                     self._widgetLoaded();
464                 }
465             });
466
467             return true;
468         }
469     });
470
471     openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
472     openils.widget.AutoFieldWidget.cache = {};
473 }
474