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 new openils.PermaCrud().retrieveAll(linkClass, {
389 async : !this.forceSync,
390 oncomplete : function(r) {
391 var list = openils.Util.readResponse(r, false, true);
401 * For widgets that run asynchronously, provide a callback for finishing up
403 _widgetLoaded : function(value) {
407 /* -------------------------------------------------------------
408 when using widgets in a grid, the cell may dissapear, which
409 kills the underlying DOM node, which causes this to fail.
410 For now, back out gracefully and let grid getters use
411 getDisplayString() instead
412 -------------------------------------------------------------*/
414 this.baseWidgetValue(this.getDisplayString());
419 this.baseWidgetValue(this.widgetValue);
420 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
421 this.widget.attr('disabled', true);
422 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
423 this.widget.attr('disabled', true);
426 this.onload(this.widget, this);
429 _buildOrgSelector : function() {
430 dojo.require('fieldmapper.OrgUtils');
431 dojo.require('openils.widget.FilteringTreeSelect');
432 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
433 this.widget.searchAttr = 'shortname';
434 this.widget.labelAttr = 'shortname';
435 this.widget.parentField = 'parent_ou';
436 var user = new openils.User();
438 if(this.widgetValue == null && this.orgDefaultsToWs)
439 this.widgetValue = user.user.ws_ou();
441 // if we have a limit perm, find the relevent orgs (async)
442 if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
445 user.getPermOrgList(this.orgLimitPerms,
447 self.widget.tree = orgList;
448 self.widget.startup();
449 self._widgetLoaded();
454 this.widget.tree = fieldmapper.aou.globalOrgTree;
455 this.widget.startup();
459 _buildPermGrpSelector : function() {
460 dojo.require('openils.widget.FilteringTreeSelect');
461 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
462 this.widget.searchAttr = 'name';
464 if(this.cache.permGrpTree) {
465 this.widget.tree = this.cache.permGrpTree;
466 this.widget.startup();
472 new openils.PermaCrud().retrieveAll('pgt', {
473 async : !this.forceSync,
474 oncomplete : function(r) {
475 var list = openils.Util.readResponse(r, false, true);
480 map[list[l].id()] = list[l];
483 var pnode = map[node.parent()];
484 if(!pnode) {root = node; continue;}
485 if(!pnode.children()) pnode.children([]);
486 pnode.children().push(node);
488 self.widget.tree = self.cache.permGrpTree = root;
489 self.widget.startup();
490 self._widgetLoaded();
497 _buildCopyLocSelector : function() {
498 dojo.require('dijit.form.FilteringSelect');
499 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
500 this.widget.searchAttr = this.widget.labalAttr = 'name';
501 this.widget.valueAttr = 'id';
503 if(this.cache.copyLocStore) {
504 this.widget.store = this.cache.copyLocStore;
505 this.widget.startup();
511 var ws_ou = openils.User.user.ws_ou();
512 var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
513 orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
516 new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
517 async : !this.forceSync,
518 oncomplete : function(r) {
519 var list = openils.Util.readResponse(r, false, true);
522 new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
523 self.cache.copyLocStore = self.widget.store;
524 self.widget.startup();
525 self._widgetLoaded();
533 openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
534 openils.widget.AutoFieldWidget.cache = {};