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");
9 dojo.declare('openils.widget.AutoFieldWidget', null, {
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 * orgDefaultsToWs -- If this is an org unit field and the widget has no value,
26 * set the value equal to the users's workstation org unit. Othwerwise, leave it null
27 * selfReference -- The primary purpose of an AutoFieldWidget is to render the value
28 * or widget for a field on an object (that may or may not link to another object).
29 * selfReference allows you to sidestep the indirection and create a selector widget
30 * based purely on an fmClass. To get a dropdown of all of the 'abc'
31 * objects, pass in {selfReference : true, fmClass : 'abc'}.
33 constructor : function(args) {
37 // find the field description in the IDL if not provided
39 this.fmClass = this.fmObject.classname;
40 this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
41 this.suppressLinkedFields = args.suppressLinkedFields || [];
43 if(this.selfReference) {
44 this.fmField = fieldmapper.IDL.fmclasses[this.fmClass].pkey;
46 // create a mock-up of the idlField object.
49 'class' : this.fmClass,
58 this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
59 var fields = this.fmIDL.fields;
61 if(fields[f].name == this.fmField)
62 this.idlField = fields[f];
67 throw new Error("AutoFieldWidget could not determine which field to render. We need more information. fmClass=" +
68 this.fmClass + ' fmField=' + this.fmField + ' fmObject=' + js2JSON(this.fmObject));
70 this.auth = openils.User.authtoken;
71 this.cache = openils.widget.AutoFieldWidget.cache;
72 this.cache[this.auth] = this.cache[this.auth] || {};
73 this.cache[this.auth].single = this.cache[this.auth].single || {};
74 this.cache[this.auth].list = this.cache[this.auth].list || {};
78 * Turn the widget-stored value into a value oils understands
80 getFormattedValue : function() {
81 var value = this.baseWidgetValue();
82 switch(this.idlField.datatype) {
85 case 'true': return 't';
86 case 'on': return 't';
87 case 'false' : return 'f';
88 case 'unset' : return null;
89 case true : return 't';
93 if(!value) return null;
94 return dojo.date.stamp.toISOString(value);
98 if(isNaN(value)) value = null;
100 return (value === '') ? null : value;
104 baseWidgetValue : function(value) {
105 var attr = (this.readOnly) ? 'content' : 'value';
106 if(arguments.length) this.widget.attr(attr, value);
107 return this.widget.attr(attr);
111 * Turn the widget-stored value into something visually suitable
113 getDisplayString : function() {
114 var value = this.widgetValue;
115 switch(this.idlField.datatype) {
120 return openils.widget.AutoFieldWidget.localeStrings.TRUE;
123 return openils.widget.AutoFieldWidget.localeStrings.FALSE;
124 case 'unset' : return openils.widget.AutoFieldWidget.localeStrings.UNSET;
125 case true : return openils.widget.AutoFieldWidget.localeStrings.TRUE;
126 default: return openils.widget.AutoFieldWidget.localeStrings.FALSE;
129 if (!value) return '';
130 dojo.require('dojo.date.locale');
131 dojo.require('dojo.date.stamp');
132 var date = dojo.date.stamp.fromISOString(value);
133 return dojo.date.locale.format(date, {formatLength:'short'});
135 if(value === null || value === undefined) return '';
136 return fieldmapper.aou.findOrgUnit(value).shortname();
139 if(isNaN(value)) value = 0;
141 if(value === undefined || value === null)
147 build : function(onload) {
149 if(this.widgetValue == null)
150 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
153 // core widget provided for us, attach and move on
154 if(this.parentNode) // may already be in the "right" place
155 this.parentNode.appendChild(this.widget.domNode);
156 if(this.widget.attr('value') == null)
157 this._widgetLoaded();
161 if(!this.parentNode) // give it somewhere to live so that dojo won't complain
162 this.parentNode = dojo.create('div');
164 this.onload = onload;
167 dojo.require('dijit.layout.ContentPane');
168 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
169 if(this.widgetValue !== null)
170 this._tryLinkedDisplayField();
172 } else if(this.widgetClass) {
173 dojo.require(this.widgetClass);
174 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
178 switch(this.idlField.datatype) {
181 dojo.require('dijit.form.TextBox');
182 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
186 this._buildOrgSelector();
190 dojo.require('dijit.form.CurrencyTextBox');
191 this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
195 dojo.require('dijit.form.NumberTextBox');
196 this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
197 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
201 dojo.require('dijit.form.NumberTextBox');
202 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
206 dojo.require('dijit.form.DateTextBox');
207 dojo.require('dojo.date.stamp');
208 this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
209 if(this.widgetValue != null)
210 this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
215 dojo.require('dijit.form.FilteringSelect');
216 var store = new dojo.data.ItemFileReadStore({
218 identifier : 'value',
220 {label : openils.widget.AutoFieldWidget.localeStrings.UNSET, value : 'unset'},
221 {label : openils.widget.AutoFieldWidget.localeStrings.TRUE, value : 'true'},
222 {label : openils.widget.AutoFieldWidget.localeStrings.FALSE, value : 'false'}
226 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
227 this.widget.searchAttr = this.widget.labelAttr = 'label';
228 this.widget.valueAttr = 'value';
229 this.widget.store = store;
230 this.widget.startup();
231 this.widgetValue = (this.widgetValue === null) ? 'unset' :
232 (openils.Util.isTrue(this.widgetValue)) ? 'true' : 'false';
234 dojo.require('dijit.form.CheckBox');
235 this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
236 this.widgetValue = openils.Util.isTrue(this.widgetValue);
241 if(this._buildLinkSelector()) break;
244 if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
245 dojo.require('dijit.form.ValidationTextBox');
246 this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
248 dojo.require('dijit.form.TextBox');
249 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
254 if(!this.async) this._widgetLoaded();
258 // we want to display the value for our widget. However, instead of displaying
259 // an ID, for exmaple, display the value for the 'selector' field on the object
261 _tryLinkedDisplayField : function(noAsync) {
263 if(this.idlField.datatype == 'org_unit')
264 return false; // we already handle org_units, no need to re-fetch
266 // user opted to bypass fetching this linked data
267 if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
270 var linkInfo = this._getLinkSelector();
271 if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector))
273 var lclass = linkInfo.linkClass;
275 if(lclass == 'aou') {
276 this.widgetValue = fieldmapper.aou.findOrgUnit(this.widgetValue).shortname();
280 // first try the store cache
282 if(this.cache[this.auth].list[lclass]) {
283 var store = this.cache[this.auth].list[lclass];
285 query[linkInfo.vfield.name] = ''+this.widgetValue;
286 store.fetch({query:query, onComplete:
288 self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
294 // then try the single object cache
295 if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
296 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
300 console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
302 // if those fail, fetch the linked object
305 new openils.PermaCrud().retrieve(lclass, this.widgetValue, {
306 async : !this.forceSync,
307 oncomplete : function(r) {
308 var item = openils.Util.readResponse(r);
309 var newvalue = item[linkInfo.vfield.selector]();
311 if(!self.cache[self.auth].single[lclass])
312 self.cache[self.auth].single[lclass] = {};
313 self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
315 self.widgetValue = newvalue;
316 self.widget.startup();
317 self._widgetLoaded();
322 _getLinkSelector : function() {
323 var linkClass = this.idlField['class'];
324 if(this.idlField.reltype != 'has_a') return false;
325 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
326 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
329 var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
331 for(var f in rclassIdl.fields) {
332 if(this.idlField.key == rclassIdl.fields[f].name) {
333 vfield = rclassIdl.fields[f];
339 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
342 linkClass : linkClass,
347 _buildLinkSelector : function() {
348 var selectorInfo = this._getLinkSelector();
349 if(!selectorInfo) return false;
351 var linkClass = selectorInfo.linkClass;
352 var vfield = selectorInfo.vfield;
356 if(linkClass == 'pgt')
357 return this._buildPermGrpSelector();
358 if(linkClass == 'aou')
359 return this._buildOrgSelector();
360 if(linkClass == 'acpl')
361 return this._buildCopyLocSelector();
364 dojo.require('dojo.data.ItemFileReadStore');
365 dojo.require('dijit.form.FilteringSelect');
367 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
368 this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
369 this.widget.valueAttr = vfield.name;
372 var oncomplete = function(list) {
375 new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
376 self.cache[self.auth].list[linkClass] = self.widget.store;
378 self.widget.store = self.cache[self.auth].list[linkClass];
380 self.widget.startup();
381 self._widgetLoaded();
384 if(this.cache[self.auth].list[linkClass]) {
388 var _cb = function(r) {
389 oncomplete(openils.Util.readResponse(r, false, true));
391 if (this.searchFilter) {
392 new openils.PermaCrud().search(linkClass, this.searchFilter, {
393 async : !this.forceSync, oncomplete : _cb
396 new openils.PermaCrud().retrieveAll(linkClass, {
397 async : !this.forceSync, oncomplete : _cb
406 * For widgets that run asynchronously, provide a callback for finishing up
408 _widgetLoaded : function(value) {
412 /* -------------------------------------------------------------
413 when using widgets in a grid, the cell may dissapear, which
414 kills the underlying DOM node, which causes this to fail.
415 For now, back out gracefully and let grid getters use
416 getDisplayString() instead
417 -------------------------------------------------------------*/
419 this.baseWidgetValue(this.getDisplayString());
424 this.baseWidgetValue(this.widgetValue);
425 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
426 this.widget.attr('disabled', true);
427 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
428 this.widget.attr('disabled', true);
431 this.onload(this.widget, this);
434 _buildOrgSelector : function() {
435 dojo.require('fieldmapper.OrgUtils');
436 dojo.require('openils.widget.FilteringTreeSelect');
437 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
438 this.widget.searchAttr = 'shortname';
439 this.widget.labelAttr = 'shortname';
440 this.widget.parentField = 'parent_ou';
441 var user = new openils.User();
443 if(this.widgetValue == null && this.orgDefaultsToWs)
444 this.widgetValue = user.user.ws_ou();
446 // if we have a limit perm, find the relevent orgs (async)
447 if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
450 user.getPermOrgList(this.orgLimitPerms,
452 self.widget.tree = orgList;
453 self.widget.startup();
454 self._widgetLoaded();
459 this.widget.tree = fieldmapper.aou.globalOrgTree;
460 this.widget.startup();
466 _buildPermGrpSelector : function() {
467 dojo.require('openils.widget.FilteringTreeSelect');
468 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
469 this.widget.searchAttr = 'name';
471 if(this.cache.permGrpTree) {
472 this.widget.tree = this.cache.permGrpTree;
473 this.widget.startup();
479 new openils.PermaCrud().retrieveAll('pgt', {
480 async : !this.forceSync,
481 oncomplete : function(r) {
482 var list = openils.Util.readResponse(r, false, true);
487 map[list[l].id()] = list[l];
490 var pnode = map[node.parent()];
491 if(!pnode) {root = node; continue;}
492 if(!pnode.children()) pnode.children([]);
493 pnode.children().push(node);
495 self.widget.tree = self.cache.permGrpTree = root;
496 self.widget.startup();
497 self._widgetLoaded();
504 _buildCopyLocSelector : function() {
505 dojo.require('dijit.form.FilteringSelect');
506 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
507 this.widget.searchAttr = this.widget.labalAttr = 'name';
508 this.widget.valueAttr = 'id';
510 if(this.cache.copyLocStore) {
511 this.widget.store = this.cache.copyLocStore;
512 this.widget.startup();
518 var ws_ou = openils.User.user.ws_ou();
519 var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
520 orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
523 new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
524 async : !this.forceSync,
525 oncomplete : function(r) {
526 var list = openils.Util.readResponse(r, false, true);
529 new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
530 self.cache.copyLocStore = self.widget.store;
531 self.widget.startup();
532 self._widgetLoaded();
540 openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
541 openils.widget.AutoFieldWidget.cache = {};