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 * selfReference -- The primary purpose of an AutoFieldWidget is to render the value
26 * or widget for a field on an object (that may or may not link to another object).
27 * selfReference allows you to sidestep the indirection and create a selector widget
28 * based purely on an fmClass. To get a dropdown of all of the 'abc'
29 * objects, pass in {selfReference : true, fmClass : 'abc'}.
31 constructor : function(args) {
35 // find the field description in the IDL if not provided
37 this.fmClass = this.fmObject.classname;
38 this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
39 this.suppressLinkedFields = args.suppressLinkedFields || [];
41 if(this.selfReference) {
42 this.fmField = fieldmapper.IDL.fmclasses[this.fmClass].pkey;
44 // create a mock-up of the idlField object.
47 'class' : this.fmClass,
56 this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
57 var fields = this.fmIDL.fields;
59 if(fields[f].name == this.fmField)
60 this.idlField = fields[f];
65 throw new Error("AutoFieldWidget could not determine which field to render. We need more information. fmClass=" +
66 this.fmClass + ' fmField=' + this.fmField + ' fmObject=' + js2JSON(this.fmObject));
68 this.auth = openils.User.authtoken;
69 this.cache = openils.widget.AutoFieldWidget.cache;
70 this.cache[this.auth] = this.cache[this.auth] || {};
71 this.cache[this.auth].single = this.cache[this.auth].single || {};
72 this.cache[this.auth].list = this.cache[this.auth].list || {};
76 * Turn the widget-stored value into a value oils understands
78 getFormattedValue : function() {
79 var value = this.baseWidgetValue();
80 switch(this.idlField.datatype) {
83 case 'true': return 't';
84 case 'on': return 't';
85 case 'false' : return 'f';
86 case 'unset' : return null;
87 case true : return 't';
91 if(!value) return null;
92 return dojo.date.stamp.toISOString(value);
96 if(isNaN(value)) value = null;
98 return (value === '') ? null : value;
102 baseWidgetValue : function(value) {
103 var attr = (this.readOnly) ? 'content' : 'value';
104 if(arguments.length) this.widget.attr(attr, value);
105 return this.widget.attr(attr);
109 * Turn the widget-stored value into something visually suitable
111 getDisplayString : function() {
112 var value = this.widgetValue;
113 switch(this.idlField.datatype) {
116 case 'true': return openils.widget.AutoFieldWidget.localeStrings.TRUE;
117 case 'false' : return openils.widget.AutoFieldWidget.localeStrings.FALSE;
118 case 'unset' : return openils.widget.AutoFieldWidget.localeStrings.UNSET;
119 case true : return openils.widget.AutoFieldWidget.localeStrings.TRUE;
120 default: return openils.widget.AutoFieldWidget.localeStrings.FALSE;
123 if (!value) return '';
124 dojo.require('dojo.date.locale');
125 dojo.require('dojo.date.stamp');
126 var date = dojo.date.stamp.fromISOString(value);
127 return dojo.date.locale.format(date, {formatLength:'short'});
129 if(value === null || value === undefined) return '';
130 return fieldmapper.aou.findOrgUnit(value).shortname();
133 if(isNaN(value)) value = 0;
135 if(value === undefined || value === null)
141 build : function(onload) {
143 if(this.widgetValue == null)
144 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
147 // core widget provided for us, attach and move on
148 if(this.parentNode) // may already be in the "right" place
149 this.parentNode.appendChild(this.widget.domNode);
150 if(this.widget.attr('value') == null)
151 this._widgetLoaded();
155 if(!this.parentNode) // give it somewhere to live so that dojo won't complain
156 this.parentNode = dojo.create('div');
158 this.onload = onload;
161 dojo.require('dijit.layout.ContentPane');
162 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
163 if(this.widgetValue !== null)
164 this._tryLinkedDisplayField();
166 } else if(this.widgetClass) {
167 dojo.require(this.widgetClass);
168 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
172 switch(this.idlField.datatype) {
175 dojo.require('dijit.form.TextBox');
176 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
180 this._buildOrgSelector();
184 dojo.require('dijit.form.CurrencyTextBox');
185 this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
189 dojo.require('dijit.form.NumberTextBox');
190 this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
191 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
195 dojo.require('dijit.form.NumberTextBox');
196 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
200 dojo.require('dijit.form.DateTextBox');
201 dojo.require('dojo.date.stamp');
202 this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
203 if(this.widgetValue != null)
204 this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
209 dojo.require('dijit.form.FilteringSelect');
210 var store = new dojo.data.ItemFileReadStore({
212 identifier : 'value',
214 {label : openils.widget.AutoFieldWidget.localeStrings.UNSET, value : 'unset'},
215 {label : openils.widget.AutoFieldWidget.localeStrings.TRUE, value : 'true'},
216 {label : openils.widget.AutoFieldWidget.localeStrings.FALSE, value : 'false'}
220 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
221 this.widget.searchAttr = this.widget.labelAttr = 'label';
222 this.widget.valueAttr = 'value';
223 this.widget.store = store;
224 this.widget.startup();
225 this.widgetValue = (this.widgetValue === null) ? 'unset' :
226 (openils.Util.isTrue(this.widgetValue)) ? 'true' : 'false';
228 dojo.require('dijit.form.CheckBox');
229 this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
230 this.widgetValue = openils.Util.isTrue(this.widgetValue);
235 if(this._buildLinkSelector()) break;
238 if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
239 dojo.require('dijit.form.ValidationTextBox');
240 this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
242 dojo.require('dijit.form.TextBox');
243 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
248 if(!this.async) this._widgetLoaded();
252 // we want to display the value for our widget. However, instead of displaying
253 // an ID, for exmaple, display the value for the 'selector' field on the object
255 _tryLinkedDisplayField : function(noAsync) {
257 if(this.idlField.datatype == 'org_unit')
258 return false; // we already handle org_units, no need to re-fetch
260 // user opted to bypass fetching this linked data
261 if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
264 var linkInfo = this._getLinkSelector();
265 if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector))
267 var lclass = linkInfo.linkClass;
269 if(lclass == 'aou') {
270 this.widgetValue = fieldmapper.aou.findOrgUnit(this.widgetValue).shortname();
274 // first try the store cache
276 if(this.cache[this.auth].list[lclass]) {
277 var store = this.cache[this.auth].list[lclass];
279 query[linkInfo.vfield.name] = ''+this.widgetValue;
280 store.fetch({query:query, onComplete:
282 self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
288 // then try the single object cache
289 if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
290 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
294 console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
296 // if those fail, fetch the linked object
299 new openils.PermaCrud().retrieve(lclass, this.widgetValue, {
300 async : !this.forceSync,
301 oncomplete : function(r) {
302 var item = openils.Util.readResponse(r);
303 var newvalue = item[linkInfo.vfield.selector]();
305 if(!self.cache[self.auth].single[lclass])
306 self.cache[self.auth].single[lclass] = {};
307 self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
309 self.widgetValue = newvalue;
310 self.widget.startup();
311 self._widgetLoaded();
316 _getLinkSelector : function() {
317 var linkClass = this.idlField['class'];
318 if(this.idlField.reltype != 'has_a') return false;
319 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
320 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
323 var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
325 for(var f in rclassIdl.fields) {
326 if(this.idlField.key == rclassIdl.fields[f].name) {
327 vfield = rclassIdl.fields[f];
333 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
336 linkClass : linkClass,
341 _buildLinkSelector : function() {
342 var selectorInfo = this._getLinkSelector();
343 if(!selectorInfo) return false;
345 var linkClass = selectorInfo.linkClass;
346 var vfield = selectorInfo.vfield;
350 if(linkClass == 'pgt')
351 return this._buildPermGrpSelector();
352 if(linkClass == 'aou')
353 return this._buildOrgSelector();
354 if(linkClass == 'acpl')
355 return this._buildCopyLocSelector();
358 dojo.require('dojo.data.ItemFileReadStore');
359 dojo.require('dijit.form.FilteringSelect');
361 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
362 this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
363 this.widget.valueAttr = vfield.name;
366 var oncomplete = function(list) {
369 new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
370 self.cache[self.auth].list[linkClass] = self.widget.store;
372 self.widget.store = self.cache[self.auth].list[linkClass];
374 self.widget.startup();
375 self._widgetLoaded();
378 if(this.cache[self.auth].list[linkClass]) {
382 new openils.PermaCrud().retrieveAll(linkClass, {
383 async : !this.forceSync,
384 oncomplete : function(r) {
385 var list = openils.Util.readResponse(r, false, true);
395 * For widgets that run asynchronously, provide a callback for finishing up
397 _widgetLoaded : function(value) {
401 /* -------------------------------------------------------------
402 when using widgets in a grid, the cell may dissapear, which
403 kills the underlying DOM node, which causes this to fail.
404 For now, back out gracefully and let grid getters use
405 getDisplayString() instead
406 -------------------------------------------------------------*/
408 this.baseWidgetValue(this.getDisplayString());
413 this.baseWidgetValue(this.widgetValue);
414 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
415 this.widget.attr('disabled', true);
416 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
417 this.widget.attr('disabled', true);
420 this.onload(this.widget, this);
423 _buildOrgSelector : function() {
424 dojo.require('fieldmapper.OrgUtils');
425 dojo.require('openils.widget.FilteringTreeSelect');
426 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
427 this.widget.searchAttr = 'shortname';
428 this.widget.labelAttr = 'shortname';
429 this.widget.parentField = 'parent_ou';
430 var user = new openils.User();
432 if(this.widgetValue == null)
433 this.widgetValue = user.user.ws_ou();
435 // if we have a limit perm, find the relevent orgs (async)
436 if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
439 user.getPermOrgList(this.orgLimitPerms,
441 self.widget.tree = orgList;
442 self.widget.startup();
443 self._widgetLoaded();
448 this.widget.tree = fieldmapper.aou.globalOrgTree;
449 this.widget.startup();
453 _buildPermGrpSelector : function() {
454 dojo.require('openils.widget.FilteringTreeSelect');
455 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
456 this.widget.searchAttr = 'name';
458 if(this.cache.permGrpTree) {
459 this.widget.tree = this.cache.permGrpTree;
460 this.widget.startup();
466 new openils.PermaCrud().retrieveAll('pgt', {
467 async : !this.forceSync,
468 oncomplete : function(r) {
469 var list = openils.Util.readResponse(r, false, true);
474 map[list[l].id()] = list[l];
477 var pnode = map[node.parent()];
478 if(!pnode) {root = node; continue;}
479 if(!pnode.children()) pnode.children([]);
480 pnode.children().push(node);
482 self.widget.tree = self.cache.permGrpTree = root;
483 self.widget.startup();
484 self._widgetLoaded();
491 _buildCopyLocSelector : function() {
492 dojo.require('dijit.form.FilteringSelect');
493 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
494 this.widget.searchAttr = this.widget.labalAttr = 'name';
495 this.widget.valueAttr = 'id';
497 if(this.cache.copyLocStore) {
498 this.widget.store = this.cache.copyLocStore;
499 this.widget.startup();
505 var ws_ou = openils.User.user.ws_ou();
506 var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
507 orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
510 new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
511 async : !this.forceSync,
512 oncomplete : function(r) {
513 var list = openils.Util.readResponse(r, false, true);
516 new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
517 self.cache.copyLocStore = self.widget.store;
518 self.widget.startup();
519 self._widgetLoaded();
527 openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
528 openils.widget.AutoFieldWidget.cache = {};