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) {
131 if(this.widgetValue == null)
132 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
135 // core widget provided for us, attach and move on
136 if(this.parentNode) // may already be in the "right" place
137 this.parentNode.appendChild(this.widget.domNode);
138 this._widgetLoaded();
142 if(!this.parentNode) // give it somewhere to live so that dojo won't complain
143 this.parentNode = dojo.create('div');
145 this.onload = onload;
148 dojo.require('dijit.layout.ContentPane');
149 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
150 if(this.widgetValue !== null)
151 this._tryLinkedDisplayField();
153 } else if(this.widgetClass) {
154 dojo.require(this.widgetClass);
155 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
159 switch(this.idlField.datatype) {
162 dojo.require('dijit.form.TextBox');
163 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
167 this._buildOrgSelector();
171 dojo.require('dijit.form.CurrencyTextBox');
172 this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
176 dojo.require('dijit.form.NumberTextBox');
177 this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
178 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
182 dojo.require('dijit.form.NumberTextBox');
183 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
187 dojo.require('dijit.form.DateTextBox');
188 dojo.require('dojo.date.stamp');
189 this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
190 if(this.widgetValue != null)
191 this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
195 dojo.require('dijit.form.CheckBox');
196 this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
197 this.widgetValue = openils.Util.isTrue(this.widgetValue);
201 if(this._buildLinkSelector()) break;
204 if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
205 dojo.require('dijit.form.ValidationTextBox');
206 this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
208 dojo.require('dijit.form.TextBox');
209 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
214 if(!this.async) this._widgetLoaded();
218 // we want to display the value for our widget. However, instead of displaying
219 // an ID, for exmaple, display the value for the 'selector' field on the object
221 _tryLinkedDisplayField : function(noAsync) {
223 if(this.idlField.datatype == 'org_unit')
224 return false; // we already handle org_units, no need to re-fetch
226 // user opted to bypass fetching this linked data
227 if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
230 var linkInfo = this._getLinkSelector();
231 if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector))
233 var lclass = linkInfo.linkClass;
235 if(lclass == 'aou') {
236 this.widgetValue = fieldmapper.aou.findOrgUnit(this.widgetValue).shortname();
240 // first try the store cache
242 if(this.cache[this.auth].list[lclass]) {
243 var store = this.cache[this.auth].list[lclass];
245 query[linkInfo.vfield.name] = ''+this.widgetValue;
246 store.fetch({query:query, onComplete:
248 self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
254 // then try the single object cache
255 if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
256 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
260 console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
262 // if those fail, fetch the linked object
265 new openils.PermaCrud().retrieve(lclass, this.widgetValue, {
266 async : !this.forceSync,
267 oncomplete : function(r) {
268 var item = openils.Util.readResponse(r);
269 var newvalue = item[linkInfo.vfield.selector]();
271 if(!self.cache[self.auth].single[lclass])
272 self.cache[self.auth].single[lclass] = {};
273 self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
275 self.widgetValue = newvalue;
276 self.widget.startup();
277 self._widgetLoaded();
282 _getLinkSelector : function() {
283 var linkClass = this.idlField['class'];
284 if(this.idlField.reltype != 'has_a') return false;
285 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
286 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
289 var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
291 for(var f in rclassIdl.fields) {
292 if(this.idlField.key == rclassIdl.fields[f].name) {
293 vfield = rclassIdl.fields[f];
299 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
302 linkClass : linkClass,
307 _buildLinkSelector : function() {
308 var selectorInfo = this._getLinkSelector();
309 if(!selectorInfo) return false;
311 var linkClass = selectorInfo.linkClass;
312 var vfield = selectorInfo.vfield;
316 if(linkClass == 'pgt')
317 return this._buildPermGrpSelector();
318 if(linkClass == 'aou')
319 return this._buildOrgSelector();
320 if(linkClass == 'acpl')
321 return this._buildCopyLocSelector();
324 dojo.require('dojo.data.ItemFileReadStore');
325 dojo.require('dijit.form.FilteringSelect');
327 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
328 this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
329 this.widget.valueAttr = vfield.name;
332 var oncomplete = function(list) {
335 new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
336 self.cache[self.auth].list[linkClass] = self.widget.store;
338 self.widget.store = self.cache[self.auth].list[linkClass];
340 self.widget.startup();
341 self._widgetLoaded();
344 if(this.cache[self.auth].list[linkClass]) {
348 new openils.PermaCrud().retrieveAll(linkClass, {
349 async : !this.forceSync,
350 oncomplete : function(r) {
351 var list = openils.Util.readResponse(r, false, true);
361 * For widgets that run asynchronously, provide a callback for finishing up
363 _widgetLoaded : function(value) {
367 /* -------------------------------------------------------------
368 when using widgets in a grid, the cell may dissapear, which
369 kills the underlying DOM node, which causes this to fail.
370 For now, back out gracefully and let grid getters use
371 getDisplayString() instead
372 -------------------------------------------------------------*/
374 this.baseWidgetValue(this.getDisplayString());
379 this.baseWidgetValue(this.widgetValue);
380 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
381 this.widget.attr('disabled', true);
382 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
383 this.widget.attr('disabled', true);
386 this.onload(this.widget, this);
389 _buildOrgSelector : function() {
390 dojo.require('fieldmapper.OrgUtils');
391 dojo.require('openils.widget.FilteringTreeSelect');
392 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
393 this.widget.searchAttr = 'shortname';
394 this.widget.labelAttr = 'shortname';
395 this.widget.parentField = 'parent_ou';
396 var user = new openils.User();
398 if(this.widgetValue == null)
399 this.widgetValue = user.user.ws_ou();
401 // if we have a limit perm, find the relevent orgs (async)
402 if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
405 user.getPermOrgList(this.orgLimitPerms,
407 self.widget.tree = orgList;
408 self.widget.startup();
409 self._widgetLoaded();
414 this.widget.tree = fieldmapper.aou.globalOrgTree;
415 this.widget.startup();
419 _buildPermGrpSelector : function() {
420 dojo.require('openils.widget.FilteringTreeSelect');
421 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
422 this.widget.searchAttr = 'name';
424 if(this.cache.permGrpTree) {
425 this.widget.tree = this.cache.permGrpTree;
426 this.widget.startup();
432 new openils.PermaCrud().retrieveAll('pgt', {
433 async : !this.forceSync,
434 oncomplete : function(r) {
435 var list = openils.Util.readResponse(r, false, true);
440 map[list[l].id()] = list[l];
443 var pnode = map[node.parent()];
444 if(!pnode) {root = node; continue;}
445 if(!pnode.children()) pnode.children([]);
446 pnode.children().push(node);
448 self.widget.tree = self.cache.permGrpTree = root;
449 self.widget.startup();
450 self._widgetLoaded();
457 _buildCopyLocSelector : function() {
458 dojo.require('dijit.form.FilteringSelect');
459 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
460 this.widget.searchAttr = this.widget.labalAttr = 'name';
461 this.widget.valueAttr = 'id';
463 if(this.cache.copyLocStore) {
464 this.widget.store = this.cache.copyLocStore;
465 this.widget.startup();
471 var ws_ou = openils.User.user.ws_ou();
472 var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
473 orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
476 new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
477 async : !this.forceSync,
478 oncomplete : function(r) {
479 var list = openils.Util.readResponse(r, false, true);
482 new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
483 self.cache.copyLocStore = self.widget.store;
484 self.widget.startup();
485 self._widgetLoaded();
493 openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
494 openils.widget.AutoFieldWidget.cache = {};