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 = 0;
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 dojo.require('dojo.date.locale');
124 dojo.require('dojo.date.stamp');
125 var date = dojo.date.stamp.fromISOString(value);
126 return dojo.date.locale.format(date, {formatLength:'short'});
128 if(value === null || value === undefined) return '';
129 return fieldmapper.aou.findOrgUnit(value).shortname();
132 if(isNaN(value)) value = 0;
134 if(value === undefined || value === null)
140 build : function(onload) {
142 if(this.widgetValue == null)
143 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
146 // core widget provided for us, attach and move on
147 if(this.parentNode) // may already be in the "right" place
148 this.parentNode.appendChild(this.widget.domNode);
149 if(this.widget.attr('value') == null)
150 this._widgetLoaded();
154 if(!this.parentNode) // give it somewhere to live so that dojo won't complain
155 this.parentNode = dojo.create('div');
157 this.onload = onload;
160 dojo.require('dijit.layout.ContentPane');
161 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
162 if(this.widgetValue !== null)
163 this._tryLinkedDisplayField();
165 } else if(this.widgetClass) {
166 dojo.require(this.widgetClass);
167 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
171 switch(this.idlField.datatype) {
174 dojo.require('dijit.form.TextBox');
175 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
179 this._buildOrgSelector();
183 dojo.require('dijit.form.CurrencyTextBox');
184 this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
188 dojo.require('dijit.form.NumberTextBox');
189 this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
190 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
194 dojo.require('dijit.form.NumberTextBox');
195 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
199 dojo.require('dijit.form.DateTextBox');
200 dojo.require('dojo.date.stamp');
201 this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
202 if(this.widgetValue != null)
203 this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
208 dojo.require('dijit.form.FilteringSelect');
209 var store = new dojo.data.ItemFileReadStore({
211 identifier : 'value',
213 {label : openils.widget.AutoFieldWidget.localeStrings.UNSET, value : 'unset'},
214 {label : openils.widget.AutoFieldWidget.localeStrings.TRUE, value : 'true'},
215 {label : openils.widget.AutoFieldWidget.localeStrings.FALSE, value : 'false'}
219 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
220 this.widget.searchAttr = this.widget.labelAttr = 'label';
221 this.widget.valueAttr = 'value';
222 this.widget.store = store;
223 this.widget.startup();
224 this.widgetValue = (this.widgetValue === null) ? 'unset' :
225 (openils.Util.isTrue(this.widgetValue)) ? 'true' : 'false';
227 dojo.require('dijit.form.CheckBox');
228 this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
229 this.widgetValue = openils.Util.isTrue(this.widgetValue);
234 if(this._buildLinkSelector()) break;
237 if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
238 dojo.require('dijit.form.ValidationTextBox');
239 this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
241 dojo.require('dijit.form.TextBox');
242 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
247 if(!this.async) this._widgetLoaded();
251 // we want to display the value for our widget. However, instead of displaying
252 // an ID, for exmaple, display the value for the 'selector' field on the object
254 _tryLinkedDisplayField : function(noAsync) {
256 if(this.idlField.datatype == 'org_unit')
257 return false; // we already handle org_units, no need to re-fetch
259 // user opted to bypass fetching this linked data
260 if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
263 var linkInfo = this._getLinkSelector();
264 if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector))
266 var lclass = linkInfo.linkClass;
268 if(lclass == 'aou') {
269 this.widgetValue = fieldmapper.aou.findOrgUnit(this.widgetValue).shortname();
273 // first try the store cache
275 if(this.cache[this.auth].list[lclass]) {
276 var store = this.cache[this.auth].list[lclass];
278 query[linkInfo.vfield.name] = ''+this.widgetValue;
279 store.fetch({query:query, onComplete:
281 self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
287 // then try the single object cache
288 if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
289 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
293 console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
295 // if those fail, fetch the linked object
298 new openils.PermaCrud().retrieve(lclass, this.widgetValue, {
299 async : !this.forceSync,
300 oncomplete : function(r) {
301 var item = openils.Util.readResponse(r);
302 var newvalue = item[linkInfo.vfield.selector]();
304 if(!self.cache[self.auth].single[lclass])
305 self.cache[self.auth].single[lclass] = {};
306 self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
308 self.widgetValue = newvalue;
309 self.widget.startup();
310 self._widgetLoaded();
315 _getLinkSelector : function() {
316 var linkClass = this.idlField['class'];
317 if(this.idlField.reltype != 'has_a') return false;
318 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
319 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
322 var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
324 for(var f in rclassIdl.fields) {
325 if(this.idlField.key == rclassIdl.fields[f].name) {
326 vfield = rclassIdl.fields[f];
332 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
335 linkClass : linkClass,
340 _buildLinkSelector : function() {
341 var selectorInfo = this._getLinkSelector();
342 if(!selectorInfo) return false;
344 var linkClass = selectorInfo.linkClass;
345 var vfield = selectorInfo.vfield;
349 if(linkClass == 'pgt')
350 return this._buildPermGrpSelector();
351 if(linkClass == 'aou')
352 return this._buildOrgSelector();
353 if(linkClass == 'acpl')
354 return this._buildCopyLocSelector();
357 dojo.require('dojo.data.ItemFileReadStore');
358 dojo.require('dijit.form.FilteringSelect');
360 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
361 this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
362 this.widget.valueAttr = vfield.name;
365 var oncomplete = function(list) {
368 new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
369 self.cache[self.auth].list[linkClass] = self.widget.store;
371 self.widget.store = self.cache[self.auth].list[linkClass];
373 self.widget.startup();
374 self._widgetLoaded();
377 if(this.cache[self.auth].list[linkClass]) {
381 new openils.PermaCrud().retrieveAll(linkClass, {
382 async : !this.forceSync,
383 oncomplete : function(r) {
384 var list = openils.Util.readResponse(r, false, true);
394 * For widgets that run asynchronously, provide a callback for finishing up
396 _widgetLoaded : function(value) {
400 /* -------------------------------------------------------------
401 when using widgets in a grid, the cell may dissapear, which
402 kills the underlying DOM node, which causes this to fail.
403 For now, back out gracefully and let grid getters use
404 getDisplayString() instead
405 -------------------------------------------------------------*/
407 this.baseWidgetValue(this.getDisplayString());
412 this.baseWidgetValue(this.widgetValue);
413 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
414 this.widget.attr('disabled', true);
415 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
416 this.widget.attr('disabled', true);
419 this.onload(this.widget, this);
422 _buildOrgSelector : function() {
423 dojo.require('fieldmapper.OrgUtils');
424 dojo.require('openils.widget.FilteringTreeSelect');
425 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
426 this.widget.searchAttr = 'shortname';
427 this.widget.labelAttr = 'shortname';
428 this.widget.parentField = 'parent_ou';
429 var user = new openils.User();
431 if(this.widgetValue == null)
432 this.widgetValue = user.user.ws_ou();
434 // if we have a limit perm, find the relevent orgs (async)
435 if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
438 user.getPermOrgList(this.orgLimitPerms,
440 self.widget.tree = orgList;
441 self.widget.startup();
442 self._widgetLoaded();
447 this.widget.tree = fieldmapper.aou.globalOrgTree;
448 this.widget.startup();
452 _buildPermGrpSelector : function() {
453 dojo.require('openils.widget.FilteringTreeSelect');
454 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
455 this.widget.searchAttr = 'name';
457 if(this.cache.permGrpTree) {
458 this.widget.tree = this.cache.permGrpTree;
459 this.widget.startup();
465 new openils.PermaCrud().retrieveAll('pgt', {
466 async : !this.forceSync,
467 oncomplete : function(r) {
468 var list = openils.Util.readResponse(r, false, true);
473 map[list[l].id()] = list[l];
476 var pnode = map[node.parent()];
477 if(!pnode) {root = node; continue;}
478 if(!pnode.children()) pnode.children([]);
479 pnode.children().push(node);
481 self.widget.tree = self.cache.permGrpTree = root;
482 self.widget.startup();
483 self._widgetLoaded();
490 _buildCopyLocSelector : function() {
491 dojo.require('dijit.form.FilteringSelect');
492 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
493 this.widget.searchAttr = this.widget.labalAttr = 'name';
494 this.widget.valueAttr = 'id';
496 if(this.cache.copyLocStore) {
497 this.widget.store = this.cache.copyLocStore;
498 this.widget.startup();
504 var ws_ou = openils.User.user.ws_ou();
505 var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
506 orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
509 new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
510 async : !this.forceSync,
511 oncomplete : function(r) {
512 var list = openils.Util.readResponse(r, false, true);
515 new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
516 self.cache.copyLocStore = self.widget.store;
517 self.widget.startup();
518 self._widgetLoaded();
526 openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
527 openils.widget.AutoFieldWidget.cache = {};