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) {
82 return (value) ? 't' : 'f'
84 if(!value) return null;
85 return dojo.date.stamp.toISOString(value);
89 if(isNaN(value)) value = 0;
91 return (value === '') ? null : value;
95 baseWidgetValue : function(value) {
96 var attr = (this.readOnly) ? 'content' : 'value';
97 if(arguments.length) this.widget.attr(attr, value);
98 return this.widget.attr(attr);
102 * Turn the widget-stored value into something visually suitable
104 getDisplayString : function() {
105 var value = this.widgetValue;
106 switch(this.idlField.datatype) {
108 return (openils.Util.isTrue(value)) ?
109 openils.widget.AutoFieldWidget.localeStrings.TRUE :
110 openils.widget.AutoFieldWidget.localeStrings.FALSE;
112 dojo.require('dojo.date.locale');
113 dojo.require('dojo.date.stamp');
114 var date = dojo.date.stamp.fromISOString(value);
115 return dojo.date.locale.format(date, {formatLength:'short'});
117 if(value === null || value === undefined) return '';
118 return fieldmapper.aou.findOrgUnit(value).shortname();
121 if(isNaN(value)) value = 0;
123 if(value === undefined || value === null)
129 build : function(onload) {
132 // core widget provided for us, attach and move on
133 if(this.parentNode) // may already be in the "right" place
134 this.parentNode.appendChild(this.widget.domNode);
138 if(!this.parentNode) // give it somewhere to live so that dojo won't complain
139 this.parentNode = dojo.create('div');
141 this.onload = onload;
142 if(this.widgetValue == null)
143 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
146 dojo.require('dijit.layout.ContentPane');
147 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
148 if(this.widgetValue !== null)
149 this._tryLinkedDisplayField();
151 } else if(this.widgetClass) {
152 dojo.require(this.widgetClass);
153 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
157 switch(this.idlField.datatype) {
160 dojo.require('dijit.form.TextBox');
161 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
165 this._buildOrgSelector();
169 dojo.require('dijit.form.CurrencyTextBox');
170 this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
174 dojo.require('dijit.form.NumberTextBox');
175 this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
176 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
180 dojo.require('dijit.form.NumberTextBox');
181 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
185 dojo.require('dijit.form.DateTextBox');
186 dojo.require('dojo.date.stamp');
187 this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
188 if(this.widgetValue != null)
189 this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
193 dojo.require('dijit.form.CheckBox');
194 this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
195 this.widgetValue = openils.Util.isTrue(this.widgetValue);
199 if(this._buildLinkSelector()) break;
202 if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
203 dojo.require('dijit.form.ValidationTextBox');
204 this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
206 dojo.require('dijit.form.TextBox');
207 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
212 if(!this.async) this._widgetLoaded();
216 // we want to display the value for our widget. However, instead of displaying
217 // an ID, for exmaple, display the value for the 'selector' field on the object
219 _tryLinkedDisplayField : function(noAsync) {
221 if(this.idlField.datatype == 'org_unit')
222 return false; // we already handle org_units, no need to re-fetch
224 // user opted to bypass fetching this linked data
225 if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
228 var linkInfo = this._getLinkSelector();
229 if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector))
231 var lclass = linkInfo.linkClass;
233 if(lclass == 'aou') {
234 this.widgetValue = fieldmapper.aou.findOrgUnit(this.widgetValue).shortname();
238 // first try the store cache
240 if(this.cache[this.auth].list[lclass]) {
241 var store = this.cache[this.auth].list[lclass];
243 query[linkInfo.vfield.name] = ''+this.widgetValue;
244 store.fetch({query:query, onComplete:
246 self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
252 // then try the single object cache
253 if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
254 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
258 console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
260 // if those fail, fetch the linked object
263 new openils.PermaCrud().retrieve(lclass, this.widgetValue, {
264 async : !this.forceSync,
265 oncomplete : function(r) {
266 var item = openils.Util.readResponse(r);
267 var newvalue = item[linkInfo.vfield.selector]();
269 if(!self.cache[self.auth].single[lclass])
270 self.cache[self.auth].single[lclass] = {};
271 self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
273 self.widgetValue = newvalue;
274 self.widget.startup();
275 self._widgetLoaded();
280 _getLinkSelector : function() {
281 var linkClass = this.idlField['class'];
282 if(this.idlField.reltype != 'has_a') return false;
283 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
284 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
287 var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
289 for(var f in rclassIdl.fields) {
290 if(this.idlField.key == rclassIdl.fields[f].name) {
291 vfield = rclassIdl.fields[f];
297 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
300 linkClass : linkClass,
305 _buildLinkSelector : function() {
306 var selectorInfo = this._getLinkSelector();
307 if(!selectorInfo) return false;
309 var linkClass = selectorInfo.linkClass;
310 var vfield = selectorInfo.vfield;
314 if(linkClass == 'pgt')
315 return this._buildPermGrpSelector();
316 if(linkClass == 'aou')
317 return this._buildOrgSelector();
318 if(linkClass == 'acpl')
319 return this._buildCopyLocSelector();
322 dojo.require('dojo.data.ItemFileReadStore');
323 dojo.require('dijit.form.FilteringSelect');
325 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
326 this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
327 this.widget.valueAttr = vfield.name;
330 var oncomplete = function(list) {
333 new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
334 self.cache[self.auth].list[linkClass] = self.widget.store;
336 self.widget.store = self.cache[self.auth].list[linkClass];
338 self.widget.startup();
339 self._widgetLoaded();
342 if(this.cache[self.auth].list[linkClass]) {
346 new openils.PermaCrud().retrieveAll(linkClass, {
347 async : !this.forceSync,
348 oncomplete : function(r) {
349 var list = openils.Util.readResponse(r, false, true);
359 * For widgets that run asynchronously, provide a callback for finishing up
361 _widgetLoaded : function(value) {
365 /* -------------------------------------------------------------
366 when using widgets in a grid, the cell may dissapear, which
367 kills the underlying DOM node, which causes this to fail.
368 For now, back out gracefully and let grid getters use
369 getDisplayString() instead
370 -------------------------------------------------------------*/
372 this.baseWidgetValue(this.getDisplayString());
377 this.baseWidgetValue(this.widgetValue);
378 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
379 this.widget.attr('disabled', true);
380 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
381 this.widget.attr('disabled', true);
384 this.onload(this.widget, this);
387 _buildOrgSelector : function() {
388 dojo.require('fieldmapper.OrgUtils');
389 dojo.require('openils.widget.FilteringTreeSelect');
390 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
391 this.widget.searchAttr = 'shortname';
392 this.widget.labelAttr = 'shortname';
393 this.widget.parentField = 'parent_ou';
394 var user = new openils.User();
396 if(this.widgetValue == null)
397 this.widgetValue = user.user.ws_ou();
399 // if we have a limit perm, find the relevent orgs (async)
400 if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
403 user.getPermOrgList(this.orgLimitPerms,
405 self.widget.tree = orgList;
406 self.widget.startup();
407 self._widgetLoaded();
412 this.widget.tree = fieldmapper.aou.globalOrgTree;
413 this.widget.startup();
417 _buildPermGrpSelector : function() {
418 dojo.require('openils.widget.FilteringTreeSelect');
419 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
420 this.widget.searchAttr = 'name';
422 if(this.cache.permGrpTree) {
423 this.widget.tree = this.cache.permGrpTree;
424 this.widget.startup();
430 new openils.PermaCrud().retrieveAll('pgt', {
431 async : !this.forceSync,
432 oncomplete : function(r) {
433 var list = openils.Util.readResponse(r, false, true);
438 map[list[l].id()] = list[l];
441 var pnode = map[node.parent()];
442 if(!pnode) {root = node; continue;}
443 if(!pnode.children()) pnode.children([]);
444 pnode.children().push(node);
446 self.widget.tree = self.cache.permGrpTree = root;
447 self.widget.startup();
448 self._widgetLoaded();
455 _buildCopyLocSelector : function() {
456 dojo.require('dijit.form.FilteringSelect');
457 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
458 this.widget.searchAttr = this.widget.labalAttr = 'name';
459 this.widget.valueAttr = 'id';
461 if(this.cache.copyLocStore) {
462 this.widget.store = this.cache.copyLocStore;
463 this.widget.startup();
469 var ws_ou = openils.User.user.ws_ou();
470 var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
471 orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
474 new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
475 async : !this.forceSync,
476 oncomplete : function(r) {
477 var list = openils.Util.readResponse(r, false, true);
480 new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
481 self.cache.copyLocStore = self.widget.store;
482 self.widget.startup();
483 self._widgetLoaded();
491 openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
492 openils.widget.AutoFieldWidget.cache = {};