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 this._widgetLoaded();
153 if(!this.parentNode) // give it somewhere to live so that dojo won't complain
154 this.parentNode = dojo.create('div');
156 this.onload = onload;
159 dojo.require('dijit.layout.ContentPane');
160 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
161 if(this.widgetValue !== null)
162 this._tryLinkedDisplayField();
164 } else if(this.widgetClass) {
165 dojo.require(this.widgetClass);
166 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
170 switch(this.idlField.datatype) {
173 dojo.require('dijit.form.TextBox');
174 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
178 this._buildOrgSelector();
182 dojo.require('dijit.form.CurrencyTextBox');
183 this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
187 dojo.require('dijit.form.NumberTextBox');
188 this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
189 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
193 dojo.require('dijit.form.NumberTextBox');
194 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
198 dojo.require('dijit.form.DateTextBox');
199 dojo.require('dojo.date.stamp');
200 this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
201 if(this.widgetValue != null)
202 this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
207 dojo.require('dijit.form.FilteringSelect');
208 var store = new dojo.data.ItemFileReadStore({
210 identifier : 'value',
212 {label : openils.widget.AutoFieldWidget.localeStrings.UNSET, value : 'unset'},
213 {label : openils.widget.AutoFieldWidget.localeStrings.TRUE, value : 'true'},
214 {label : openils.widget.AutoFieldWidget.localeStrings.FALSE, value : 'false'}
218 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
219 this.widget.searchAttr = this.widget.labelAttr = 'label';
220 this.widget.valueAttr = 'value';
221 this.widget.store = store;
222 this.widget.startup();
223 this.widgetValue = (this.widgetValue === null) ? 'unset' :
224 (openils.Util.isTrue(this.widgetValue)) ? 'true' : 'false';
226 dojo.require('dijit.form.CheckBox');
227 this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
228 this.widgetValue = openils.Util.isTrue(this.widgetValue);
233 if(this._buildLinkSelector()) break;
236 if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
237 dojo.require('dijit.form.ValidationTextBox');
238 this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
240 dojo.require('dijit.form.TextBox');
241 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
246 if(!this.async) this._widgetLoaded();
250 // we want to display the value for our widget. However, instead of displaying
251 // an ID, for exmaple, display the value for the 'selector' field on the object
253 _tryLinkedDisplayField : function(noAsync) {
255 if(this.idlField.datatype == 'org_unit')
256 return false; // we already handle org_units, no need to re-fetch
258 // user opted to bypass fetching this linked data
259 if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
262 var linkInfo = this._getLinkSelector();
263 if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector))
265 var lclass = linkInfo.linkClass;
267 if(lclass == 'aou') {
268 this.widgetValue = fieldmapper.aou.findOrgUnit(this.widgetValue).shortname();
272 // first try the store cache
274 if(this.cache[this.auth].list[lclass]) {
275 var store = this.cache[this.auth].list[lclass];
277 query[linkInfo.vfield.name] = ''+this.widgetValue;
278 store.fetch({query:query, onComplete:
280 self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
286 // then try the single object cache
287 if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
288 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
292 console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
294 // if those fail, fetch the linked object
297 new openils.PermaCrud().retrieve(lclass, this.widgetValue, {
298 async : !this.forceSync,
299 oncomplete : function(r) {
300 var item = openils.Util.readResponse(r);
301 var newvalue = item[linkInfo.vfield.selector]();
303 if(!self.cache[self.auth].single[lclass])
304 self.cache[self.auth].single[lclass] = {};
305 self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
307 self.widgetValue = newvalue;
308 self.widget.startup();
309 self._widgetLoaded();
314 _getLinkSelector : function() {
315 var linkClass = this.idlField['class'];
316 if(this.idlField.reltype != 'has_a') return false;
317 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
318 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
321 var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
323 for(var f in rclassIdl.fields) {
324 if(this.idlField.key == rclassIdl.fields[f].name) {
325 vfield = rclassIdl.fields[f];
331 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
334 linkClass : linkClass,
339 _buildLinkSelector : function() {
340 var selectorInfo = this._getLinkSelector();
341 if(!selectorInfo) return false;
343 var linkClass = selectorInfo.linkClass;
344 var vfield = selectorInfo.vfield;
348 if(linkClass == 'pgt')
349 return this._buildPermGrpSelector();
350 if(linkClass == 'aou')
351 return this._buildOrgSelector();
352 if(linkClass == 'acpl')
353 return this._buildCopyLocSelector();
356 dojo.require('dojo.data.ItemFileReadStore');
357 dojo.require('dijit.form.FilteringSelect');
359 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
360 this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
361 this.widget.valueAttr = vfield.name;
364 var oncomplete = function(list) {
367 new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
368 self.cache[self.auth].list[linkClass] = self.widget.store;
370 self.widget.store = self.cache[self.auth].list[linkClass];
372 self.widget.startup();
373 self._widgetLoaded();
376 if(this.cache[self.auth].list[linkClass]) {
380 new openils.PermaCrud().retrieveAll(linkClass, {
381 async : !this.forceSync,
382 oncomplete : function(r) {
383 var list = openils.Util.readResponse(r, false, true);
393 * For widgets that run asynchronously, provide a callback for finishing up
395 _widgetLoaded : function(value) {
399 /* -------------------------------------------------------------
400 when using widgets in a grid, the cell may dissapear, which
401 kills the underlying DOM node, which causes this to fail.
402 For now, back out gracefully and let grid getters use
403 getDisplayString() instead
404 -------------------------------------------------------------*/
406 this.baseWidgetValue(this.getDisplayString());
411 this.baseWidgetValue(this.widgetValue);
412 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
413 this.widget.attr('disabled', true);
414 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
415 this.widget.attr('disabled', true);
418 this.onload(this.widget, this);
421 _buildOrgSelector : function() {
422 dojo.require('fieldmapper.OrgUtils');
423 dojo.require('openils.widget.FilteringTreeSelect');
424 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
425 this.widget.searchAttr = 'shortname';
426 this.widget.labelAttr = 'shortname';
427 this.widget.parentField = 'parent_ou';
428 var user = new openils.User();
430 if(this.widgetValue == null)
431 this.widgetValue = user.user.ws_ou();
433 // if we have a limit perm, find the relevent orgs (async)
434 if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
437 user.getPermOrgList(this.orgLimitPerms,
439 self.widget.tree = orgList;
440 self.widget.startup();
441 self._widgetLoaded();
446 this.widget.tree = fieldmapper.aou.globalOrgTree;
447 this.widget.startup();
451 _buildPermGrpSelector : function() {
452 dojo.require('openils.widget.FilteringTreeSelect');
453 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
454 this.widget.searchAttr = 'name';
456 if(this.cache.permGrpTree) {
457 this.widget.tree = this.cache.permGrpTree;
458 this.widget.startup();
464 new openils.PermaCrud().retrieveAll('pgt', {
465 async : !this.forceSync,
466 oncomplete : function(r) {
467 var list = openils.Util.readResponse(r, false, true);
472 map[list[l].id()] = list[l];
475 var pnode = map[node.parent()];
476 if(!pnode) {root = node; continue;}
477 if(!pnode.children()) pnode.children([]);
478 pnode.children().push(node);
480 self.widget.tree = self.cache.permGrpTree = root;
481 self.widget.startup();
482 self._widgetLoaded();
489 _buildCopyLocSelector : function() {
490 dojo.require('dijit.form.FilteringSelect');
491 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
492 this.widget.searchAttr = this.widget.labalAttr = 'name';
493 this.widget.valueAttr = 'id';
495 if(this.cache.copyLocStore) {
496 this.widget.store = this.cache.copyLocStore;
497 this.widget.startup();
503 var ws_ou = openils.User.user.ws_ou();
504 var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
505 orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
508 new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
509 async : !this.forceSync,
510 oncomplete : function(r) {
511 var list = openils.Util.readResponse(r, false, true);
514 new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
515 self.cache.copyLocStore = self.widget.store;
516 self.widget.startup();
517 self._widgetLoaded();
525 openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
526 openils.widget.AutoFieldWidget.cache = {};