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 'false' : return 'f';
85 case 'unset' : return null;
86 case true : return 't';
90 if(!value) return null;
91 return dojo.date.stamp.toISOString(value);
95 if(isNaN(value)) value = 0;
97 return (value === '') ? null : value;
101 baseWidgetValue : function(value) {
102 var attr = (this.readOnly) ? 'content' : 'value';
103 if(arguments.length) this.widget.attr(attr, value);
104 return this.widget.attr(attr);
108 * Turn the widget-stored value into something visually suitable
110 getDisplayString : function() {
111 var value = this.widgetValue;
112 switch(this.idlField.datatype) {
115 case 'true': return openils.widget.AutoFieldWidget.localeStrings.TRUE;
116 case 'false' : return openils.widget.AutoFieldWidget.localeStrings.FALSE;
117 case 'unset' : return openils.widget.AutoFieldWidget.localeStrings.UNSET;
118 case true : return openils.widget.AutoFieldWidget.localeStrings.TRUE;
119 default: return openils.widget.AutoFieldWidget.localeStrings.FALSE;
122 dojo.require('dojo.date.locale');
123 dojo.require('dojo.date.stamp');
124 var date = dojo.date.stamp.fromISOString(value);
125 return dojo.date.locale.format(date, {formatLength:'short'});
127 if(value === null || value === undefined) return '';
128 return fieldmapper.aou.findOrgUnit(value).shortname();
131 if(isNaN(value)) value = 0;
133 if(value === undefined || value === null)
139 build : function(onload) {
141 if(this.widgetValue == null)
142 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
145 // core widget provided for us, attach and move on
146 if(this.parentNode) // may already be in the "right" place
147 this.parentNode.appendChild(this.widget.domNode);
148 this._widgetLoaded();
152 if(!this.parentNode) // give it somewhere to live so that dojo won't complain
153 this.parentNode = dojo.create('div');
155 this.onload = onload;
158 dojo.require('dijit.layout.ContentPane');
159 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
160 if(this.widgetValue !== null)
161 this._tryLinkedDisplayField();
163 } else if(this.widgetClass) {
164 dojo.require(this.widgetClass);
165 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
169 switch(this.idlField.datatype) {
172 dojo.require('dijit.form.TextBox');
173 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
177 this._buildOrgSelector();
181 dojo.require('dijit.form.CurrencyTextBox');
182 this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
186 dojo.require('dijit.form.NumberTextBox');
187 this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
188 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
192 dojo.require('dijit.form.NumberTextBox');
193 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
197 dojo.require('dijit.form.DateTextBox');
198 dojo.require('dojo.date.stamp');
199 this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
200 if(this.widgetValue != null)
201 this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
206 dojo.require('dijit.form.FilteringSelect');
207 var store = new dojo.data.ItemFileReadStore({
209 identifier : 'value',
211 {label : openils.widget.AutoFieldWidget.localeStrings.UNSET, value : 'unset'},
212 {label : openils.widget.AutoFieldWidget.localeStrings.TRUE, value : 'true'},
213 {label : openils.widget.AutoFieldWidget.localeStrings.FALSE, value : 'false'}
217 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
218 this.widget.searchAttr = this.widget.labelAttr = 'label';
219 this.widget.valueAttr = 'value';
220 this.widget.store = store;
221 this.widget.startup();
222 this.widgetValue = (this.widgetValue === null) ? 'unset' :
223 (openils.Util.isTrue(this.widgetValue)) ? 'true' : 'false';
225 dojo.require('dijit.form.CheckBox');
226 this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
227 this.widgetValue = openils.Util.isTrue(this.widgetValue);
232 if(this._buildLinkSelector()) break;
235 if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
236 dojo.require('dijit.form.ValidationTextBox');
237 this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
239 dojo.require('dijit.form.TextBox');
240 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
245 if(!this.async) this._widgetLoaded();
249 // we want to display the value for our widget. However, instead of displaying
250 // an ID, for exmaple, display the value for the 'selector' field on the object
252 _tryLinkedDisplayField : function(noAsync) {
254 if(this.idlField.datatype == 'org_unit')
255 return false; // we already handle org_units, no need to re-fetch
257 // user opted to bypass fetching this linked data
258 if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
261 var linkInfo = this._getLinkSelector();
262 if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector))
264 var lclass = linkInfo.linkClass;
266 if(lclass == 'aou') {
267 this.widgetValue = fieldmapper.aou.findOrgUnit(this.widgetValue).shortname();
271 // first try the store cache
273 if(this.cache[this.auth].list[lclass]) {
274 var store = this.cache[this.auth].list[lclass];
276 query[linkInfo.vfield.name] = ''+this.widgetValue;
277 store.fetch({query:query, onComplete:
279 self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
285 // then try the single object cache
286 if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
287 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
291 console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
293 // if those fail, fetch the linked object
296 new openils.PermaCrud().retrieve(lclass, this.widgetValue, {
297 async : !this.forceSync,
298 oncomplete : function(r) {
299 var item = openils.Util.readResponse(r);
300 var newvalue = item[linkInfo.vfield.selector]();
302 if(!self.cache[self.auth].single[lclass])
303 self.cache[self.auth].single[lclass] = {};
304 self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
306 self.widgetValue = newvalue;
307 self.widget.startup();
308 self._widgetLoaded();
313 _getLinkSelector : function() {
314 var linkClass = this.idlField['class'];
315 if(this.idlField.reltype != 'has_a') return false;
316 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
317 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
320 var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
322 for(var f in rclassIdl.fields) {
323 if(this.idlField.key == rclassIdl.fields[f].name) {
324 vfield = rclassIdl.fields[f];
330 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
333 linkClass : linkClass,
338 _buildLinkSelector : function() {
339 var selectorInfo = this._getLinkSelector();
340 if(!selectorInfo) return false;
342 var linkClass = selectorInfo.linkClass;
343 var vfield = selectorInfo.vfield;
347 if(linkClass == 'pgt')
348 return this._buildPermGrpSelector();
349 if(linkClass == 'aou')
350 return this._buildOrgSelector();
351 if(linkClass == 'acpl')
352 return this._buildCopyLocSelector();
355 dojo.require('dojo.data.ItemFileReadStore');
356 dojo.require('dijit.form.FilteringSelect');
358 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
359 this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
360 this.widget.valueAttr = vfield.name;
363 var oncomplete = function(list) {
366 new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
367 self.cache[self.auth].list[linkClass] = self.widget.store;
369 self.widget.store = self.cache[self.auth].list[linkClass];
371 self.widget.startup();
372 self._widgetLoaded();
375 if(this.cache[self.auth].list[linkClass]) {
379 new openils.PermaCrud().retrieveAll(linkClass, {
380 async : !this.forceSync,
381 oncomplete : function(r) {
382 var list = openils.Util.readResponse(r, false, true);
392 * For widgets that run asynchronously, provide a callback for finishing up
394 _widgetLoaded : function(value) {
398 /* -------------------------------------------------------------
399 when using widgets in a grid, the cell may dissapear, which
400 kills the underlying DOM node, which causes this to fail.
401 For now, back out gracefully and let grid getters use
402 getDisplayString() instead
403 -------------------------------------------------------------*/
405 this.baseWidgetValue(this.getDisplayString());
410 this.baseWidgetValue(this.widgetValue);
411 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
412 this.widget.attr('disabled', true);
413 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
414 this.widget.attr('disabled', true);
417 this.onload(this.widget, this);
420 _buildOrgSelector : function() {
421 dojo.require('fieldmapper.OrgUtils');
422 dojo.require('openils.widget.FilteringTreeSelect');
423 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
424 this.widget.searchAttr = 'shortname';
425 this.widget.labelAttr = 'shortname';
426 this.widget.parentField = 'parent_ou';
427 var user = new openils.User();
429 if(this.widgetValue == null)
430 this.widgetValue = user.user.ws_ou();
432 // if we have a limit perm, find the relevent orgs (async)
433 if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
436 user.getPermOrgList(this.orgLimitPerms,
438 self.widget.tree = orgList;
439 self.widget.startup();
440 self._widgetLoaded();
445 this.widget.tree = fieldmapper.aou.globalOrgTree;
446 this.widget.startup();
450 _buildPermGrpSelector : function() {
451 dojo.require('openils.widget.FilteringTreeSelect');
452 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
453 this.widget.searchAttr = 'name';
455 if(this.cache.permGrpTree) {
456 this.widget.tree = this.cache.permGrpTree;
457 this.widget.startup();
463 new openils.PermaCrud().retrieveAll('pgt', {
464 async : !this.forceSync,
465 oncomplete : function(r) {
466 var list = openils.Util.readResponse(r, false, true);
471 map[list[l].id()] = list[l];
474 var pnode = map[node.parent()];
475 if(!pnode) {root = node; continue;}
476 if(!pnode.children()) pnode.children([]);
477 pnode.children().push(node);
479 self.widget.tree = self.cache.permGrpTree = root;
480 self.widget.startup();
481 self._widgetLoaded();
488 _buildCopyLocSelector : function() {
489 dojo.require('dijit.form.FilteringSelect');
490 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
491 this.widget.searchAttr = this.widget.labalAttr = 'name';
492 this.widget.valueAttr = 'id';
494 if(this.cache.copyLocStore) {
495 this.widget.store = this.cache.copyLocStore;
496 this.widget.startup();
502 var ws_ou = openils.User.user.ws_ou();
503 var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
504 orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
507 new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
508 async : !this.forceSync,
509 oncomplete : function(r) {
510 var list = openils.Util.readResponse(r, false, true);
513 new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
514 self.cache.copyLocStore = self.widget.store;
515 self.widget.startup();
516 self._widgetLoaded();
524 openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
525 openils.widget.AutoFieldWidget.cache = {};