92bf2ea83d90a6ba20091027ef8e037c8fba2a54
[working/Evergreen.git] / Open-ILS / xul / staff_client / server / admin / org_unit_settings.js
1 dojo.require('fieldmapper.AutoIDL');
2 dojo.require('dijit.layout.LayoutContainer');
3 dojo.require('dijit.layout.ContentPane');
4 dojo.require('dijit.form.FilteringSelect');
5 dojo.require('dijit.Dialog');
6 dojo.require('dijit.form.Textarea');
7 dojo.require('dijit.form.ComboBox');
8 dojo.require('dojox.grid.Grid');
9 dojo.require('fieldmapper.Fieldmapper');
10 dojo.require('fieldmapper.dojoData');
11 dojo.require('fieldmapper.OrgUtils');
12 dojo.require('dojo.cookie');
13 dojo.require('openils.CGI');
14 dojo.require('openils.User');
15 dojo.require('openils.Event');
16 dojo.require('openils.widget.OrgUnitFilteringSelect');
17 dojo.require('openils.PermaCrud');
18 dojo.require('openils.widget.AutoFieldWidget');
19 dojo.require('openils.widget.ProgressDialog');
20 dojo.require('dijit.Toolbar');
21 dojo.require('openils.XUL');
22
23 var authtoken;
24 var query;
25 var contextOrg;
26 var user;
27 var osSettings = {};
28 var ouSettingValues = {};
29 var ouSettingNames = {};
30 var ouNames = {};
31 var osEditAutoWidget;
32 var perm_codes = {};
33 var osGroups = {};
34 var searchAssist = [];
35 var pcrud;
36
37 function osInit(data) {
38     showProcessingDialog(true);
39     
40     authtoken = new openils.CGI().param('ses') || dojo.cookie('ses');
41     if(!authtoken && openils.XUL.isXUL()) {
42         var stash = openils.XUL.getStash();
43         authtoken = stash.session.key;
44     }   
45     query = new openils.CGI().param('filter');
46     user = new openils.User({authtoken:authtoken});
47     contextOrg = user.user.ws_ou();
48     openils.User.authtoken = authtoken;
49     
50     pcrud = new openils.PermaCrud({authtoken:authtoken});
51     
52     var grps = pcrud.retrieveAll('csg');
53     dojo.forEach(grps, function(grp) { osGroups[grp.name()] = grp.label(); });
54     
55     var connect = function() { 
56         dojo.connect(contextOrg, 'onChange', osChangeContext); 
57
58         // don't draw the org settings grid unless the user has permission
59         // to view org settings in at least 1 org unit
60         osContextSelector.store.fetch({query: {}, start: 0, count: 0, 
61             onBegin: function(size) { 
62                 if(size) { osDraw();  return; }
63                 dojo.removeClass('no-perms', 'hide_me');
64             }
65         });
66         
67     };
68
69     new openils.User().buildPermOrgSelector('VIEW_ORG_SETTINGS', osContextSelector, null, connect);
70
71     fieldmapper.standardRequest(
72         [   'open-ils.actor',
73             'open-ils.actor.permissions.retrieve'],
74         {   async: true,
75             oncomplete: function(r) {
76                 var data = r.recv().content();
77                 if(e = openils.Event.parse(data))
78                     return alert(e);
79                 for(var key in data)
80                     perm_codes[data[key].id()] = data[key].code();
81             }
82         }
83     );
84     
85     var aous = pcrud.retrieveAll('aou');
86     dojo.forEach(aous, function(ou) { ouNames[ou.id()] = ou.shortname(); });
87     
88     showProcessingDialog(false);
89 }
90 dojo.addOnLoad(osInit);
91
92 function showProcessingDialog(toggle) {
93     var proc = dojo.byId('proci18n').innerHTML;
94     if(toggle)
95         progressDialog.show(true, proc);
96     else
97         progressDialog.hide();
98 }
99
100 function osDraw(specific_setting) {
101     showProcessingDialog(true);
102
103     var names = [];
104     if (specific_setting) {
105
106         for(var key in specific_setting)
107             names.push(key);
108
109     } else {
110         var types = new openils.PermaCrud({authtoken:authtoken}).retrieveAll('coust');
111
112         searchAssist =  [];
113         
114         dojo.forEach(types, 
115             function(type) {
116                 osSettings[type.name()] = {
117                     label : type.label(),
118                     desc : type.description(),
119                     type : type.datatype(),
120                     fm_class : type.fm_class(),
121                     update_perm : type.update_perm(),
122                     grp : osGroups[type.grp()]
123                 }
124                 
125                 var tmp = "" + type.label() + "" + type.description() + "" + type.fm_class() + "" + 
126                           osGroups[type.grp()] + "" + type.name();
127                 
128                 searchAssist[type.name()] = tmp.toLowerCase().replace(/[^a-z0-9]+/g, '');
129             }
130         );
131         
132         for(var key in osSettings)
133             names.push(key);
134     }
135     
136     osDrawNames(names);
137 }
138
139 /**
140  * Auto searches 500ms after entering text.
141  */
142 var osCurrentSearchTimeout;
143 function osSearchChange() {
144     if(osCurrentSearchTimeout != null)
145         clearTimeout(osCurrentSearchTimeout);
146         
147     osCurrentSearchTimeout = setTimeout("doSearch()", 500);
148 }
149
150 //Limits those functions seen to the ones that have similar text to 
151 //that which is provided. Not case sensitive.
152 function osLimitSeen(text) {
153     showProcessingDialog(true);
154     
155     text = text.split(' ');
156     
157     for(t in text)
158         text[t] = text[t].toLowerCase().replace(/[^a-z0-9]+/g, '');
159     
160     numTerms = text.length;
161     
162     var names = [];
163     for(var n in searchAssist) {
164         var numFound = 0;
165         
166         for(var t in text) 
167             if(searchAssist[n].indexOf(text[t]) != -1)
168                 numFound++;
169                 
170         if(numFound == numTerms)
171             names.push(n);
172     }
173     
174     //Don't update on an empty list as this causes bizarre errors.
175     if(names.length == 0) {
176         showProcessingDialog(false);
177         showAlert(dojo.byId('noresults').innerHTML);
178         return;
179     }
180     
181     ouSettingValues = {}; // Clear the values.
182     osDrawNames(names); // Repopulate setting values with the ones we want.
183 }
184
185 function doSearch() {
186     osCurrentSearchTimeout = null;
187     
188     var query = dojo.byId('searchBox').value;
189     
190     osLimitSeen(query);
191     
192     return false; //Keep form from submitting
193 }
194
195 function clearSearch() {
196     if(dojo.byId('searchBox').value != '') { // Don't refresh on blank.
197         dojo.byId('searchBox').value = '';
198         doSearch();
199     }
200 }
201
202 function osToJson() {
203     var out = dojo.fromJson(dojo.toJson(ouSettingValues)); // Easy deep copy
204     var context = osContextSelector.getValue();
205     
206     // Set all of the nulls in the outputs to be part of the current org
207     // this keeps from overwriting later if this file is transfered to another lib.
208     for(key in out)
209         if(out[key] == null)
210             out[key] = {'org':context, 'value':null};
211     
212     dojo.byId('jsonOutput').value = dojo.toJson(out);
213     osJSONOutDialog.show();
214 }
215
216 // Copies the text from the json output to the clipboard.
217 function osJsonOutputCopy() {
218     document.popupNode = dojo.byId('jsonOutput');
219     dojo.byId('jsonOutput').focus();
220     dojo.byId('jsonOutput').select();
221     util.clipboard.copy();
222     showAlert(dojo.byId('os-copy').innerHTML);
223 }
224
225 function osJsonInputPaste() {
226     document.popupNode = dojo.byId('jsonInput');
227     document.popupNode.focus();
228     document.popupNode.select();
229     util.clipboard.paste();
230 }
231
232 function osFromJson() {
233      dojo.byId('jsonInput').value = '';
234      osJSONInDialog.show();
235 }
236
237 function osFromJsonSubmit() {
238     var input = dojo.byId('jsonInput').value;
239     var from = dojo.fromJson(input);
240     
241     osJSONInDialog.hide();
242
243     showProcessingDialog(true);
244     for(key in from) {
245         
246         //Check that there isn't already set to the same value (speed increase);
247         if( ouSettingValues[key] == null && 
248             from[key]['value'] == null &&
249             osContextSelector.getValue() == from[key]['org'])
250             continue;
251                 
252         if( ouSettingValues[key] != null && 
253             ouSettingValues[key]['value'] == from[key]['value'] &&
254             ouSettingValues[key]['org'] == from[key]['org'])
255             continue;
256         
257         var obj = {};
258         var context;
259         
260         if(from[key] != null) { 
261             obj[key] = from[key]['value'];
262             context  = from[key]['org'];
263         }
264         
265         osUpdateSetting(obj, context);
266     }
267     showProcessingDialog(false);
268 }
269
270 //Draws the grid based upon a given array of items to draw.
271 function osDrawNames(names) {
272     fieldmapper.standardRequest(
273         [   'open-ils.actor', 
274             'open-ils.actor.ou_setting.ancestor_default.batch'],
275         {   async: true,
276             params: [contextOrg, names, authtoken],
277             oncomplete: function(r) {
278                 var data = r.recv().content();
279                 if(e = openils.Event.parse(data))
280                     return alert(e);
281                 for(var key in data)
282                     ouSettingValues[key] = data[key];
283                 osLoadGrid(ouSettingValues);
284                 
285                 showProcessingDialog(false);
286             }
287         }
288     );
289 }
290
291 function osChangeContext() {
292     if(contextOrg == osContextSelector.getValue())
293         return;
294     contextOrg = osContextSelector.getValue();
295     osDraw();
296 }
297
298 function osLoadGrid(data) {
299     var gridData = {items:[]}
300     for(var key in data) {
301         var setting = osSettings[key];
302         setting.context = null;
303         setting.value = null;
304         if(data[key]) {
305             setting.context = data[key].org;
306             setting.value = data[key].value;
307         }
308         gridData.items.push({name:key});
309     }
310     gridData.items = gridData.items.sort(
311         function(a, b) {
312             var seta = osSettings[a.name];
313             var setb = osSettings[b.name];
314             if(seta.grp + "" + seta.label > setb.grp + "" + setb.label) return 1;
315             if(seta.grp + "" + seta.label < setb.grp + "" + setb.label) return -1;
316             return 0;
317         }
318     );
319     gridData.identifier = 'name';
320     var store = new dojo.data.ItemFileReadStore({data:gridData});
321     var model = new dojox.grid.data.DojoData(
322         null, store, {rowsPerPage: 100, clientSort: true, query:{name:'*'}});
323
324     osGrid.setModel(model);
325     osGrid.setStructure(osGridLayout);
326     osGrid.update();
327 }
328
329 function osGetGridData(rowIdx) {
330     var data = this.grid.model.getRow(rowIdx);
331     if(!data) return '';
332     var setting = osSettings[data.name];
333     var value = setting[this.field];
334     if(value == null) return '';
335
336     switch(this.field) {
337         case 'context':
338             return fieldmapper.aou.findOrgUnit(value).shortname();
339         case 'label':
340             if(setting.noInherit)
341                 return value + ' *';
342             return value;
343         case 'value':
344             if(setting.fm_class) {
345                 var autoWidget = new openils.widget.AutoFieldWidget(
346                     {
347                         fmClass : setting.fm_class,
348                         selfReference : true,
349                         widgetValue : value,
350                         forceSync : true,
351                         readOnly : true
352                     }
353                 );
354                 autoWidget.build();
355                 if(autoWidget.getDisplayString())
356                     return autoWidget.getDisplayString();
357             }
358
359             if(setting.type == 'bool') {
360                 if(value) 
361                     return dojo.byId('os-true').innerHTML;
362                 return dojo.byId('os-false').innerHTML;
363             }
364         default:
365             return value;
366     }
367 }
368
369 function osGetEditLink(rowIdx) {
370     var data = this.grid.model.getRow(rowIdx);
371     if(!data) return '';
372     return data.name;
373 }
374
375 function osFormatEditLink(name) {
376     return this.value.replace(/SETTING/g, name);
377 }
378
379 function osLaunchEditor(name) {
380     osEditDialog._osattr = name;
381     osEditDialog.show();
382     var perms = ['UPDATE_ORG_UNIT_SETTING_ALL'];
383     if(osSettings[name].update_perm && perm_codes[osSettings[name].update_perm]) {
384         perms.push(perm_codes[osSettings[name].update_perm]);
385     }
386     user.buildPermOrgSelector(
387         perms,
388         osEditContextSelector, osSettings[name].context
389     );
390     dojo.byId('os-edit-name').innerHTML = osSettings[name].label;
391     dojo.byId('os-edit-desc').innerHTML = osSettings[name].desc || '';
392
393     dojo.style(osEditTextBox.domNode, 'display', 'none');
394     dojo.style(osEditCurrencyTextBox.domNode, 'display', 'none');
395     dojo.style(osEditNumberTextBox.domNode, 'display', 'none');
396     dojo.style(osEditBoolSelect.domNode, 'display', 'none');
397
398     var fmClass = osSettings[name].fm_class;
399
400     if(osEditAutoWidget) {
401         osEditAutoWidget.domNode.parentNode.removeChild(osEditAutoWidget.domNode);
402         osEditAutoWidget.destroy();
403         osEditAutoWidget = null;
404     }
405     
406     if(fmClass) {
407
408         if(osEditAutoWidget) {
409             osEditAutoWidget.domNode.parentNode.removeChild(osEditAutoWidget.domNode);
410             osEditAutoWidget.destroy();
411         }
412
413         var autoWidget = new openils.widget.AutoFieldWidget(
414             {
415                 fmClass : fmClass,
416                 selfReference : true,
417                 parentNode : dojo.create('div', null, dojo.byId('os-edit-auto-widget')),
418                 widgetValue : osSettings[name].value
419             }
420         );
421         autoWidget.build(
422             function(w) {
423                 osEditAutoWidget = w;
424             }
425         );
426
427     } else {
428         var widget;
429         switch(osSettings[name].type) {
430             case 'number':
431                 widget = osEditNumberTextBox; 
432                 break;
433             case 'currency':
434                 widget = osEditCurrencyTextBox; 
435                 break;
436             case 'bool':
437                 widget = osEditBoolSelect; 
438                 break;
439             default:
440                 widget = osEditTextBox;
441         }
442
443         dojo.style(widget.domNode, 'display', 'block');
444         widget.setValue(osSettings[name].value);
445     }
446 }
447
448 function osEditSetting(deleteMe) {
449     osEditDialog.hide();
450     var name = osEditDialog._osattr;
451
452     var obj = {};
453     if(deleteMe) {
454         obj[name] = null;
455     } else {
456         if(osSettings[name].fm_class) {
457             var val = osEditAutoWidget.attr('value');
458             osEditAutoWidget.domNode.parentNode.removeChild(osEditAutoWidget.domNode);
459             osEditAutoWidget.destroy();
460             osEditAutoWidget = null;
461             if(val == null || val == '') return;
462             obj[name] = val;
463
464         } else {
465             switch(osSettings[name].type) {
466                 case 'number':
467                     obj[name] = osEditNumberTextBox.getValue();
468                     if(obj[name] == null) return;
469                     break;
470                 case 'currency':
471                     obj[name] = osEditCurrencyTextBox.getValue();
472                     if(obj[name] == null) return;
473                     break;
474                 case 'bool':
475                     var val = osEditBoolSelect.getValue();
476                     obj[name] = (val == 'true') ? true : false;
477                     break;
478                 default:
479                     obj[name] = osEditTextBox.getValue();
480                     if(obj[name] == null) return;
481             }
482         }
483     }
484     osUpdateSetting(obj, osEditContextSelector.getValue(), name);
485 }
486
487 function osUpdateSetting(obj, context, name) {
488     fieldmapper.standardRequest(
489         ['open-ils.actor', 'open-ils.actor.org_unit.settings.update'],
490         {   async: true,
491             params: [authtoken, context, obj],
492             oncomplete: function(r) {
493                 var res = r.recv().content();
494                 if(e = openils.Event.parse(res))
495                     return alert(e);
496                 osDraw(obj);
497                 if(context != osContextSelector.getValue())
498                     showAlert(dojo.byId('os-not-chosen').innerHTML);
499             }
500         }
501     );
502 }
503
504 function osRevertSetting(context, name, value) {
505     osHistDialog.hide();
506
507     var obj = {};
508     
509     if(value == 'null' || value == null)
510         obj[name] = null;
511     else
512         obj[name] = value;
513     
514     osUpdateSetting(obj, context, name);
515 }
516
517 function osGetHistoryLink(rowIdx) {
518     var data = this.grid.model.getRow(rowIdx);
519     if(!data) return '';
520     return data.name;
521 }
522
523 function osFormatHistoryLink(name) {
524     return this.value.replace(/SETTING/, name);
525 }
526
527 function osLaunchHistory(name) {
528     showProcessingDialog(true);
529     
530     dojo.byId('osHistName').innerHTML = osSettings[name].label;
531     
532     var data = dojo.byId('histTitle').innerHTML;
533     var thisHist = pcrud.search('coustl', {'field_name':name});
534     for(var i in thisHist.reverse()) {
535         d = thisHist[i].date_applied();
536         a = ouNames[thisHist[i].org()];
537         o = thisHist[i].original_value();
538         if(o) o=o.replace(/\&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
539         n = thisHist[i].new_value();
540         if(n) n=n.replace(/\&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
541         r = thisHist[i].org();
542         // Table is: Date | Org Name | Orig Value | New Value | Revert
543         data += "<tr><td>" + d + "</td><td>" + a + "</td><td>" + o +
544         "</td><td>" + n + "</td><td>" +
545         "<a href='javascript:void(0);' onclick='osRevertSetting(" + r + ", &quot;" + name +"&quot;,"+o+");'>"+dojo.byId('os-revert').innerHTML+"</a></td></tr>";
546     }
547         
548     dojo.byId('historyData').innerHTML = data;
549     
550     showProcessingDialog(false);
551     osHistDialog.show();
552 }
553
554 function showAlert(message, timeout) {
555     if(timeout == null) {
556         timeout = 3000;
557         if(message.length > 50)
558             timeout = 5000;
559         if(message.length > 80)
560             timeout = 8000;
561     }
562     
563     dojo.removeClass('msgCont', 'hidden');
564     
565     dojo.byId('msgInner').innerHTML = message;
566     
567     var fadeArgs = { node: "msgCont" };
568     dojo.fadeIn(fadeArgs).play();
569     
570     window.setTimeout('hideAlert()', timeout);
571     
572 }
573
574 function hideAlert() {
575     var fadeArgs = { node: "msgCont" };
576     dojo.fadeOut(fadeArgs).play();
577     dojo.addClass('msgCont', 'hidden');
578 }