LP#1244547 Maintain Filter Settings on Library Settings Editor
[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     if(dojo.byId('isFiltered') == 'true') {
137         osLimitSeen(dojo.byId('searchBox').value);
138     } else {
139         osDrawNames(names);
140     }
141 }
142
143 /**
144  * Auto searches 500ms after entering text.
145  */
146 var osCurrentSearchTimeout;
147 function osSearchChange() {
148     if(osCurrentSearchTimeout != null)
149         clearTimeout(osCurrentSearchTimeout);
150
151     dojo.byId('isFiltered').value = 'true';
152     osCurrentSearchTimeout = setTimeout("doSearch()", 500);
153 }
154
155 //Limits those functions seen to the ones that have similar text to 
156 //that which is provided. Not case sensitive.
157 function osLimitSeen(text) {
158     showProcessingDialog(true);
159     
160     text = text.split(' ');
161     
162     for(t in text)
163         text[t] = text[t].toLowerCase().replace(/[^a-z0-9]+/g, '');
164     
165     numTerms = text.length;
166     
167     var names = [];
168     for(var n in searchAssist) {
169         var numFound = 0;
170         
171         for(var t in text) 
172             if(searchAssist[n].indexOf(text[t]) != -1)
173                 numFound++;
174                 
175         if(numFound == numTerms)
176             names.push(n);
177     }
178     
179     //Don't update on an empty list as this causes bizarre errors.
180     if(names.length == 0) {
181         showProcessingDialog(false);
182         showAlert(dojo.byId('noresults').innerHTML);
183         return;
184     }
185     
186     ouSettingValues = {}; // Clear the values.
187     osDrawNames(names); // Repopulate setting values with the ones we want.
188 }
189
190 function doSearch() {
191     osCurrentSearchTimeout = null;
192     
193     var query = dojo.byId('searchBox').value;
194     
195     osLimitSeen(query);
196     
197     return false; //Keep form from submitting
198 }
199
200 function clearSearch() {
201     dojo.byId('isFiltered').value = 'false';
202     if(dojo.byId('searchBox').value != '') { // Don't refresh on blank.
203         dojo.byId('searchBox').value = '';
204         doSearch();
205     }
206 }
207
208 function osToJson() {
209     var out = dojo.fromJson(dojo.toJson(ouSettingValues)); // Easy deep copy
210     var context = osContextSelector.getValue();
211     
212     // Set all of the nulls in the outputs to be part of the current org
213     // this keeps from overwriting later if this file is transfered to another lib.
214     for(key in out)
215         if(out[key] == null)
216             out[key] = {'org':context, 'value':null};
217     
218     dojo.byId('jsonOutput').value = dojo.toJson(out);
219     osJSONOutDialog.show();
220 }
221
222 // Copies the text from the json output to the clipboard.
223 function osJsonOutputCopy() {
224     document.popupNode = dojo.byId('jsonOutput');
225     dojo.byId('jsonOutput').focus();
226     dojo.byId('jsonOutput').select();
227     util.clipboard.copy();
228     showAlert(dojo.byId('os-copy').innerHTML);
229 }
230
231 function osJsonInputPaste() {
232     document.popupNode = dojo.byId('jsonInput');
233     document.popupNode.focus();
234     document.popupNode.select();
235     util.clipboard.paste();
236 }
237
238 function osFromJson() {
239      dojo.byId('jsonInput').value = '';
240      osJSONInDialog.show();
241 }
242
243 function osFromJsonSubmit() {
244     var input = dojo.byId('jsonInput').value;
245     var from = dojo.fromJson(input);
246     
247     osJSONInDialog.hide();
248
249     showProcessingDialog(true);
250     for(key in from) {
251         
252         //Check that there isn't already set to the same value (speed increase);
253         if( ouSettingValues[key] == null && 
254             from[key]['value'] == null &&
255             osContextSelector.getValue() == from[key]['org'])
256             continue;
257                 
258         if( ouSettingValues[key] != null && 
259             ouSettingValues[key]['value'] == from[key]['value'] &&
260             ouSettingValues[key]['org'] == from[key]['org'])
261             continue;
262         
263         var obj = {};
264         var context;
265         
266         if(from[key] != null) { 
267             obj[key] = from[key]['value'];
268             context  = from[key]['org'];
269         }
270         
271         osUpdateSetting(obj, context);
272     }
273     showProcessingDialog(false);
274 }
275
276 //Draws the grid based upon a given array of items to draw.
277 function osDrawNames(names) {
278     fieldmapper.standardRequest(
279         [   'open-ils.actor', 
280             'open-ils.actor.ou_setting.ancestor_default.batch'],
281         {   async: true,
282             params: [contextOrg, names, authtoken],
283             oncomplete: function(r) {
284                 var data = r.recv().content();
285                 if(e = openils.Event.parse(data))
286                     return alert(e);
287                 for(var key in data)
288                     ouSettingValues[key] = data[key];
289                 osLoadGrid(ouSettingValues);
290                 
291                 showProcessingDialog(false);
292             }
293         }
294     );
295 }
296
297 function osChangeContext() {
298     if(contextOrg == osContextSelector.getValue())
299         return;
300     contextOrg = osContextSelector.getValue();
301     osDraw();
302 }
303
304 function osLoadGrid(data) {
305     var gridData = {items:[]}
306     for(var key in data) {
307         var setting = osSettings[key];
308         setting.context = null;
309         setting.value = null;
310         if(data[key]) {
311             setting.context = data[key].org;
312             setting.value = data[key].value;
313         }
314         gridData.items.push({name:key});
315     }
316     gridData.items = gridData.items.sort(
317         function(a, b) {
318             var seta = osSettings[a.name];
319             var setb = osSettings[b.name];
320             if(seta.grp + "" + seta.label > setb.grp + "" + setb.label) return 1;
321             if(seta.grp + "" + seta.label < setb.grp + "" + setb.label) return -1;
322             return 0;
323         }
324     );
325     gridData.identifier = 'name';
326     var store = new dojo.data.ItemFileReadStore({data:gridData});
327     var model = new dojox.grid.data.DojoData(
328         null, store, {rowsPerPage: 100, clientSort: true, query:{name:'*'}});
329
330     osGrid.setModel(model);
331     osGrid.setStructure(osGridLayout);
332     osGrid.update();
333 }
334
335 function osGetGridData(rowIdx) {
336     var data = this.grid.model.getRow(rowIdx);
337     if(!data) return '';
338     var setting = osSettings[data.name];
339     var value = setting[this.field];
340     if(value == null) return '';
341
342     switch(this.field) {
343         case 'context':
344             return fieldmapper.aou.findOrgUnit(value).shortname();
345         case 'label':
346             if(setting.noInherit)
347                 return value + ' *';
348             return value;
349         case 'value':
350             if(setting.fm_class) {
351                 var autoWidget = new openils.widget.AutoFieldWidget(
352                     {
353                         fmClass : setting.fm_class,
354                         selfReference : true,
355                         widgetValue : value,
356                         forceSync : true,
357                         readOnly : true
358                     }
359                 );
360                 autoWidget.build();
361                 if(autoWidget.getDisplayString())
362                     return autoWidget.getDisplayString();
363             }
364
365             if(setting.type == 'bool') {
366                 if(value) 
367                     return dojo.byId('os-true').innerHTML;
368                 return dojo.byId('os-false').innerHTML;
369             }
370         default:
371             return value;
372     }
373 }
374
375 function osGetEditLink(rowIdx) {
376     var data = this.grid.model.getRow(rowIdx);
377     if(!data) return '';
378     return data.name;
379 }
380
381 function osFormatEditLink(name) {
382     return this.value.replace(/SETTING/g, name);
383 }
384
385 function osLaunchEditor(name) {
386     osEditDialog._osattr = name;
387     osEditDialog.show();
388     var perms = ['UPDATE_ORG_UNIT_SETTING_ALL'];
389     if(osSettings[name].update_perm && perm_codes[osSettings[name].update_perm]) {
390         perms.push(perm_codes[osSettings[name].update_perm]);
391     }
392     user.buildPermOrgSelector(
393         perms,
394         osEditContextSelector, osSettings[name].context
395     );
396     dojo.byId('os-edit-name').innerHTML = osSettings[name].label;
397     dojo.byId('os-edit-desc').innerHTML = osSettings[name].desc || '';
398
399     dojo.style(osEditTextBox.domNode, 'display', 'none');
400     dojo.style(osEditCurrencyTextBox.domNode, 'display', 'none');
401     dojo.style(osEditNumberTextBox.domNode, 'display', 'none');
402     dojo.style(osEditBoolSelect.domNode, 'display', 'none');
403
404     var fmClass = osSettings[name].fm_class;
405
406     if(osEditAutoWidget) {
407         osEditAutoWidget.domNode.parentNode.removeChild(osEditAutoWidget.domNode);
408         osEditAutoWidget.destroy();
409         osEditAutoWidget = null;
410     }
411     
412     if(fmClass) {
413
414         if(osEditAutoWidget) {
415             osEditAutoWidget.domNode.parentNode.removeChild(osEditAutoWidget.domNode);
416             osEditAutoWidget.destroy();
417         }
418
419         var autoWidget = new openils.widget.AutoFieldWidget(
420             {
421                 fmClass : fmClass,
422                 selfReference : true,
423                 parentNode : dojo.create('div', null, dojo.byId('os-edit-auto-widget')),
424                 widgetValue : osSettings[name].value
425             }
426         );
427         autoWidget.build(
428             function(w) {
429                 osEditAutoWidget = w;
430             }
431         );
432
433     } else {
434         var widget;
435         switch(osSettings[name].type) {
436             case 'number':
437                 widget = osEditNumberTextBox; 
438                 break;
439             case 'currency':
440                 widget = osEditCurrencyTextBox; 
441                 break;
442             case 'bool':
443                 widget = osEditBoolSelect; 
444                 break;
445             default:
446                 widget = osEditTextBox;
447         }
448
449         dojo.style(widget.domNode, 'display', 'block');
450         widget.setValue(osSettings[name].value);
451     }
452 }
453
454 function osEditSetting(deleteMe) {
455     osEditDialog.hide();
456     var name = osEditDialog._osattr;
457
458     var obj = {};
459     if(deleteMe) {
460         obj[name] = null;
461     } else {
462         if(osSettings[name].fm_class) {
463             var val = osEditAutoWidget.attr('value');
464             osEditAutoWidget.domNode.parentNode.removeChild(osEditAutoWidget.domNode);
465             osEditAutoWidget.destroy();
466             osEditAutoWidget = null;
467             if(val == null || val == '') return;
468             obj[name] = val;
469
470         } else {
471             switch(osSettings[name].type) {
472                 case 'number':
473                     obj[name] = osEditNumberTextBox.getValue();
474                     if(obj[name] == null) return;
475                     break;
476                 case 'currency':
477                     obj[name] = osEditCurrencyTextBox.getValue();
478                     if(obj[name] == null) return;
479                     break;
480                 case 'bool':
481                     var val = osEditBoolSelect.getValue();
482                     obj[name] = (val == 'true') ? true : false;
483                     break;
484                 default:
485                     obj[name] = osEditTextBox.getValue();
486                     if(obj[name] == null) return;
487             }
488         }
489     }
490     osUpdateSetting(obj, osEditContextSelector.getValue(), name);
491 }
492
493 function osUpdateSetting(obj, context, name) {
494     fieldmapper.standardRequest(
495         ['open-ils.actor', 'open-ils.actor.org_unit.settings.update'],
496         {   async: true,
497             params: [authtoken, context, obj],
498             oncomplete: function(r) {
499                 var res = r.recv().content();
500                 if(e = openils.Event.parse(res))
501                     return alert(e);
502                 osDraw(obj);
503                 if(context != osContextSelector.getValue())
504                     showAlert(dojo.byId('os-not-chosen').innerHTML);
505             }
506         }
507     );
508 }
509
510 function osRevertSetting(context, name, value) {
511     osHistDialog.hide();
512
513     var obj = {};
514     
515     if(value == 'null' || value == null)
516         obj[name] = null;
517     else
518         obj[name] = value;
519     
520     osUpdateSetting(obj, context, name);
521 }
522
523 function osGetHistoryLink(rowIdx) {
524     var data = this.grid.model.getRow(rowIdx);
525     if(!data) return '';
526     return data.name;
527 }
528
529 function osFormatHistoryLink(name) {
530     return this.value.replace(/SETTING/, name);
531 }
532
533 function osLaunchHistory(name) {
534     showProcessingDialog(true);
535     
536     dojo.byId('osHistName').innerHTML = osSettings[name].label;
537     
538     var data = dojo.byId('histTitle').innerHTML;
539     var thisHist = pcrud.search('coustl', {'field_name':name});
540     for(var i in thisHist.reverse()) {
541         d = thisHist[i].date_applied();
542         a = ouNames[thisHist[i].org()];
543         o = thisHist[i].original_value();
544         if(o) o=o.replace(/\&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
545         n = thisHist[i].new_value();
546         if(n) n=n.replace(/\&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
547         r = thisHist[i].org();
548         // Table is: Date | Org Name | Orig Value | New Value | Revert
549         data += "<tr><td>" + d + "</td><td>" + a + "</td><td>" + o +
550         "</td><td>" + n + "</td><td>" +
551         "<a href='javascript:void(0);' onclick='osRevertSetting(" + r + ", &quot;" + name +"&quot;,"+o+");'>"+dojo.byId('os-revert').innerHTML+"</a></td></tr>";
552     }
553         
554     dojo.byId('historyData').innerHTML = data;
555     
556     showProcessingDialog(false);
557     osHistDialog.show();
558 }
559
560 function showAlert(message, timeout) {
561     if(timeout == null) {
562         timeout = 3000;
563         if(message.length > 50)
564             timeout = 5000;
565         if(message.length > 80)
566             timeout = 8000;
567     }
568     
569     dojo.removeClass('msgCont', 'hidden');
570     
571     dojo.byId('msgInner').innerHTML = message;
572     
573     var fadeArgs = { node: "msgCont" };
574     dojo.fadeIn(fadeArgs).play();
575     
576     window.setTimeout('hideAlert()', timeout);
577     
578 }
579
580 function hideAlert() {
581     var fadeArgs = { node: "msgCont" };
582     dojo.fadeOut(fadeArgs).play();
583     dojo.addClass('msgCont', 'hidden');
584 }