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 = null;
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) {
118 return openils.widget.AutoFieldWidget.localeStrings.TRUE;
121 return openils.widget.AutoFieldWidget.localeStrings.FALSE;
122 case 'unset' : return openils.widget.AutoFieldWidget.localeStrings.UNSET;
123 case true : return openils.widget.AutoFieldWidget.localeStrings.TRUE;
124 default: return openils.widget.AutoFieldWidget.localeStrings.FALSE;
127 if (!value) return '';
128 dojo.require('dojo.date.locale');
129 dojo.require('dojo.date.stamp');
130 var date = dojo.date.stamp.fromISOString(value);
131 return dojo.date.locale.format(date, {formatLength:'short'});
133 if(value === null || value === undefined) return '';
134 return fieldmapper.aou.findOrgUnit(value).shortname();
137 if(isNaN(value)) value = 0;
139 if(value === undefined || value === null)
145 build : function(onload) {
147 if(this.widgetValue == null)
148 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
151 // core widget provided for us, attach and move on
152 if(this.parentNode) // may already be in the "right" place
153 this.parentNode.appendChild(this.widget.domNode);
154 if(this.widget.attr('value') == null)
155 this._widgetLoaded();
159 if(!this.parentNode) // give it somewhere to live so that dojo won't complain
160 this.parentNode = dojo.create('div');
162 this.onload = onload;
165 dojo.require('dijit.layout.ContentPane');
166 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
167 if(this.widgetValue !== null)
168 this._tryLinkedDisplayField();
170 } else if(this.widgetClass) {
171 dojo.require(this.widgetClass);
172 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
176 switch(this.idlField.datatype) {
179 dojo.require('dijit.form.TextBox');
180 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
184 this._buildOrgSelector();
188 dojo.require('dijit.form.CurrencyTextBox');
189 this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
193 dojo.require('dijit.form.NumberTextBox');
194 this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
195 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
199 dojo.require('dijit.form.NumberTextBox');
200 this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
204 dojo.require('dijit.form.DateTextBox');
205 dojo.require('dojo.date.stamp');
206 this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
207 if(this.widgetValue != null)
208 this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
213 dojo.require('dijit.form.FilteringSelect');
214 var store = new dojo.data.ItemFileReadStore({
216 identifier : 'value',
218 {label : openils.widget.AutoFieldWidget.localeStrings.UNSET, value : 'unset'},
219 {label : openils.widget.AutoFieldWidget.localeStrings.TRUE, value : 'true'},
220 {label : openils.widget.AutoFieldWidget.localeStrings.FALSE, value : 'false'}
224 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
225 this.widget.searchAttr = this.widget.labelAttr = 'label';
226 this.widget.valueAttr = 'value';
227 this.widget.store = store;
228 this.widget.startup();
229 this.widgetValue = (this.widgetValue === null) ? 'unset' :
230 (openils.Util.isTrue(this.widgetValue)) ? 'true' : 'false';
232 dojo.require('dijit.form.CheckBox');
233 this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
234 this.widgetValue = openils.Util.isTrue(this.widgetValue);
239 if(this._buildLinkSelector()) break;
242 if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
243 dojo.require('dijit.form.ValidationTextBox');
244 this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
246 dojo.require('dijit.form.TextBox');
247 this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
252 if(!this.async) this._widgetLoaded();
256 // we want to display the value for our widget. However, instead of displaying
257 // an ID, for exmaple, display the value for the 'selector' field on the object
259 _tryLinkedDisplayField : function(noAsync) {
261 if(this.idlField.datatype == 'org_unit')
262 return false; // we already handle org_units, no need to re-fetch
264 // user opted to bypass fetching this linked data
265 if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
268 var linkInfo = this._getLinkSelector();
269 if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector))
271 var lclass = linkInfo.linkClass;
273 if(lclass == 'aou') {
274 this.widgetValue = fieldmapper.aou.findOrgUnit(this.widgetValue).shortname();
278 // first try the store cache
280 if(this.cache[this.auth].list[lclass]) {
281 var store = this.cache[this.auth].list[lclass];
283 query[linkInfo.vfield.name] = ''+this.widgetValue;
284 store.fetch({query:query, onComplete:
286 self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
292 // then try the single object cache
293 if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
294 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
298 console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
300 // if those fail, fetch the linked object
303 new openils.PermaCrud().retrieve(lclass, this.widgetValue, {
304 async : !this.forceSync,
305 oncomplete : function(r) {
306 var item = openils.Util.readResponse(r);
307 var newvalue = item[linkInfo.vfield.selector]();
309 if(!self.cache[self.auth].single[lclass])
310 self.cache[self.auth].single[lclass] = {};
311 self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
313 self.widgetValue = newvalue;
314 self.widget.startup();
315 self._widgetLoaded();
320 _getLinkSelector : function() {
321 var linkClass = this.idlField['class'];
322 if(this.idlField.reltype != 'has_a') return false;
323 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
324 if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
327 var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
329 for(var f in rclassIdl.fields) {
330 if(this.idlField.key == rclassIdl.fields[f].name) {
331 vfield = rclassIdl.fields[f];
337 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
340 linkClass : linkClass,
345 _buildLinkSelector : function() {
346 var selectorInfo = this._getLinkSelector();
347 if(!selectorInfo) return false;
349 var linkClass = selectorInfo.linkClass;
350 var vfield = selectorInfo.vfield;
354 if(linkClass == 'pgt')
355 return this._buildPermGrpSelector();
356 if(linkClass == 'aou')
357 return this._buildOrgSelector();
358 if(linkClass == 'acpl')
359 return this._buildCopyLocSelector();
362 dojo.require('dojo.data.ItemFileReadStore');
363 dojo.require('dijit.form.FilteringSelect');
365 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
366 this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
367 this.widget.valueAttr = vfield.name;
370 var oncomplete = function(list) {
373 new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
374 self.cache[self.auth].list[linkClass] = self.widget.store;
376 self.widget.store = self.cache[self.auth].list[linkClass];
378 self.widget.startup();
379 self._widgetLoaded();
382 if(this.cache[self.auth].list[linkClass]) {
386 new openils.PermaCrud().retrieveAll(linkClass, {
387 async : !this.forceSync,
388 oncomplete : function(r) {
389 var list = openils.Util.readResponse(r, false, true);
399 * For widgets that run asynchronously, provide a callback for finishing up
401 _widgetLoaded : function(value) {
405 /* -------------------------------------------------------------
406 when using widgets in a grid, the cell may dissapear, which
407 kills the underlying DOM node, which causes this to fail.
408 For now, back out gracefully and let grid getters use
409 getDisplayString() instead
410 -------------------------------------------------------------*/
412 this.baseWidgetValue(this.getDisplayString());
417 this.baseWidgetValue(this.widgetValue);
418 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
419 this.widget.attr('disabled', true);
420 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
421 this.widget.attr('disabled', true);
424 this.onload(this.widget, this);
427 _buildOrgSelector : function() {
428 dojo.require('fieldmapper.OrgUtils');
429 dojo.require('openils.widget.FilteringTreeSelect');
430 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
431 this.widget.searchAttr = 'shortname';
432 this.widget.labelAttr = 'shortname';
433 this.widget.parentField = 'parent_ou';
434 var user = new openils.User();
436 if(this.widgetValue == null)
437 this.widgetValue = user.user.ws_ou();
439 // if we have a limit perm, find the relevent orgs (async)
440 if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
443 user.getPermOrgList(this.orgLimitPerms,
445 self.widget.tree = orgList;
446 self.widget.startup();
447 self._widgetLoaded();
452 this.widget.tree = fieldmapper.aou.globalOrgTree;
453 this.widget.startup();
457 _buildPermGrpSelector : function() {
458 dojo.require('openils.widget.FilteringTreeSelect');
459 this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
460 this.widget.searchAttr = 'name';
462 if(this.cache.permGrpTree) {
463 this.widget.tree = this.cache.permGrpTree;
464 this.widget.startup();
470 new openils.PermaCrud().retrieveAll('pgt', {
471 async : !this.forceSync,
472 oncomplete : function(r) {
473 var list = openils.Util.readResponse(r, false, true);
478 map[list[l].id()] = list[l];
481 var pnode = map[node.parent()];
482 if(!pnode) {root = node; continue;}
483 if(!pnode.children()) pnode.children([]);
484 pnode.children().push(node);
486 self.widget.tree = self.cache.permGrpTree = root;
487 self.widget.startup();
488 self._widgetLoaded();
495 _buildCopyLocSelector : function() {
496 dojo.require('dijit.form.FilteringSelect');
497 this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
498 this.widget.searchAttr = this.widget.labalAttr = 'name';
499 this.widget.valueAttr = 'id';
501 if(this.cache.copyLocStore) {
502 this.widget.store = this.cache.copyLocStore;
503 this.widget.startup();
509 var ws_ou = openils.User.user.ws_ou();
510 var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
511 orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
514 new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
515 async : !this.forceSync,
516 oncomplete : function(r) {
517 var list = openils.Util.readResponse(r, false, true);
520 new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
521 self.cache.copyLocStore = self.widget.store;
522 self.widget.startup();
523 self._widgetLoaded();
531 openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
532 openils.widget.AutoFieldWidget.cache = {};