]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/reports/xul/template-config.js
Improve Firefox/XULRunner Support
[working/Evergreen.git] / Open-ILS / web / reports / xul / template-config.js
1 dojo.requireLocalization("openils.reports", "reports");
2
3 var rpt_strings = dojo.i18n.getLocalization("openils.reports", "reports");
4
5 function removeReportAtom (args) {
6         if (!args) args = {};
7
8         var active_tab = filterByAttribute(
9                 $('used-source-fields-tabbox').getElementsByTagName('tab'),
10                 'selected',
11                 'true'
12         )[0];
13         var tabname = active_tab.getAttribute('id');
14
15         var tabpanel = $( tabname + 'panel' );
16         var tree = tabpanel.getElementsByTagName('tree')[0];
17         var fields = getSelectedItems(tree);
18
19
20         for (var i in fields) {
21                 var field = fields[i];
22                 var colname = field.firstChild.firstChild.nextSibling.getAttribute('label');
23                 var relation_alias = field.getAttribute('relation');
24
25                 delete rpt_rel_cache[relation_alias].fields[tabname][colname];
26                 if (tabname == 'dis_tab') {
27                         var _o_tmp = [];
28                         for each (var _o_col in rpt_rel_cache.order_by) {
29                                 if (_o_col.relation == relation_alias && _o_col.field == colname) continue;
30                                 _o_tmp.push( _o_col );
31                         }
32                         rpt_rel_cache.order_by = _o_tmp
33                 }
34
35                 with (rpt_rel_cache[relation_alias].fields) {
36                         if ( getKeys(dis_tab).length == 0 && getKeys(filter_tab).length == 0 && getKeys(aggfilter_tab).length == 0 )
37                                 delete rpt_rel_cache[relation_alias];
38                 }
39         }
40
41         renderSources();
42
43         return true;
44 }
45
46 function getSourceDefinition(aClass) {
47         var class_obj = getIDLClass(aClass);
48         return class_obj.getAttributeNS(persistNS,'tablename') || 
49         '(' + class_obj.getElementsByTagNameNS(persistNS,'source_definition')[0].textContent + ')';
50 }
51
52 function addReportAtoms () {
53         var nope = $( 'source-add' ).getAttribute('disabled');
54         if (nope == 'true') return false;
55
56         var class_tree = $('class-view');
57         var transform_tree = $('trans-view');
58
59         var active_tab = filterByAttribute(
60                 $('used-source-fields-tabbox').getElementsByTagName('tab'),
61                 'selected',
62                 'true'
63         )[0];
64
65         var tabname = active_tab.getAttribute('id')
66
67         var items = getSelectedItems(class_tree);
68         var transform = getSelectedItems(transform_tree)[0];
69
70         var reltype = $('path-label').getAttribute('reltype');
71         var class_label = $('path-label').value;
72         var relation_alias = hex_md5(class_label);
73
74         for (var i in items) {
75                 var item = items[i];
76
77                 var class_path = item.getAttribute('fullpath');
78                 var field_class = item.getAttribute('idlclass');
79                 var datatype = item.getAttribute('datatype');
80                 var colname = item.getAttribute('idlfield');
81                 var jointype = item.getAttribute('join');
82                 var field_label = item.firstChild.firstChild.getAttribute('label');
83
84                 var table_name = getSourceDefinition(field_class);
85
86                 if ( !rpt_rel_cache[relation_alias] ) {
87                         rpt_rel_cache[relation_alias] =
88                                 { label     : class_label,
89                                   alias     : relation_alias,
90                                   path      : class_path,
91                                   join      : jointype,
92                                   reltype   : reltype,
93                                   idlclass  : field_class,
94                                   table     : table_name,
95                                   fields    : { dis_tab : {}, filter_tab : {}, aggfilter_tab : {} }
96                                 };
97                 }
98
99                 if ( !rpt_rel_cache[relation_alias].fields[tabname][colname] ) {
100                         rpt_rel_cache[relation_alias].fields[tabname][colname] =
101                                 { colname   : colname,
102                                   transform : (transform && transform.getAttribute('name')) || rpt_strings.TEMPLATE_CONF_BARE,
103                                   aggregate : transform && transform.getAttribute('aggregate'),
104                                   params    : transform && transform.getAttribute('params'),
105                                   transform_label: (transform && transform.getAttribute('alias')) || rpt_strings.TEMPLATE_CONF_RAW_DATA,
106                                   alias     : field_label,
107                                   join      : jointype,
108                                   datatype  : datatype,
109                                   op        : (datatype == 'array') ? '= any' : '=',
110                                   op_label  : rpt_strings.TEMPLATE_CONF_EQUALS,
111                                   op_value  : {}
112                                 };
113
114                         if (!rpt_rel_cache.order_by)
115                                 rpt_rel_cache.order_by = [];
116
117                         if (tabname == 'dis_tab')
118                                 rpt_rel_cache.order_by.push( { relation : relation_alias, field : colname } );
119
120                 } else if (confirm(dojo.string.substitute( rpt_strings.TEMPLATE_CONF_CONFIRM_RESET, [field_label, class_label] )) ) {
121                         rpt_rel_cache[relation_alias].fields[tabname][colname] =
122                                 { colname   : colname,
123                                   transform : (transform && transform.getAttribute('name')) || rpt_strings.TEMPLATE_CONF_BARE,
124                                   aggregate : transform && transform.getAttribute('aggregate'),
125                                   params    : transform && transform.getAttribute('params'),
126                                   transform_label: (transform && transform.getAttribute('alias')) || rpt_strings.TEMPLATE_CONF_RAW_DATA,
127                                   alias     : field_label,
128                                   join      : jointype,
129                                   datatype  : datatype,
130                                   op        : '=',
131                                   op_label  : rpt_strings.TEMPLATE_CONF_EQUALS,
132                                   op_value  : {}
133                                 };
134                 }
135         }
136
137         renderSources();
138
139         return true;
140 }
141
142 function changeDisplayOrder (dir) {
143         var active_tab = filterByAttribute(
144                 $('used-source-fields-tabbox').getElementsByTagName('tab'),
145                 'selected',
146                 'true'
147         )[0];
148
149         var tabname = active_tab.getAttribute('id');
150
151         var tabpanel = $( tabname + 'panel' );
152         var tree = tabpanel.getElementsByTagName('tree')[0];
153         var item = getSelectedItems(tree)[0];
154
155         var item_pos = tree.view.selection.currentIndex;
156
157         if (dir == 'u') {
158                 if ( item.previousSibling ) {
159                         item.parentNode.insertBefore( item, item.previousSibling );
160                         item_pos--;
161                 }
162         } else if (dir == 'd') {
163                 if ( item.nextSibling ) {
164                         if (item.nextSibling.nextSibling ) item.parentNode.insertBefore( item, item.nextSibling.nextSibling );
165                         else item.parentNode.appendChild( item );
166                         item_pos++;
167                 }
168         }
169         
170         rpt_rel_cache.order_by = [];
171         var ordered_list = tree.getElementsByTagName('treeitem');
172         for (var i = 0; i < ordered_list.length; i++) {
173                 rpt_rel_cache.order_by.push(
174                         { relation : ordered_list[i].getAttribute('relation'),
175                           field    : ordered_list[i].firstChild.firstChild.nextSibling.getAttribute('label')
176                         }
177                 );
178         }
179
180         tree.view.selection.select( item_pos );
181         return true;
182 }
183
184 function alterColumnLabel () {
185         var active_tab = filterByAttribute(
186                 $('used-source-fields-tabbox').getElementsByTagName('tab'),
187                 'selected',
188                 'true'
189         )[0];
190
191         var tabname = active_tab.getAttribute('id');
192
193         var tabpanel = $( tabname + 'panel' );
194         var tree = tabpanel.getElementsByTagName('tree')[0];
195         var item_pos = tree.view.selection.currentIndex;
196
197         var item = getSelectedItems(tree)[0];
198         var relation_alias = item.getAttribute('relation');
199
200         var field = item.firstChild.firstChild;
201         var colname = field.nextSibling.getAttribute('label');
202
203         var new_label = prompt( dojo.string.substitute(rpt_strings.TEMPLATE_CONF_PROMPT_CHANGE, [field.getAttribute("label")]) );
204
205         if (new_label) {
206                 rpt_rel_cache[relation_alias].fields[tabname][colname].alias = new_label;
207                 renderSources(true);
208                 tree.view.selection.select( item_pos );
209                 tree.focus();
210                 tree.click();
211         }
212
213         return true;
214 }
215
216 function alterColumnTransform (trans) {
217
218         var transform = OILS_RPT_TRANSFORMS[trans];
219
220         var active_tab = filterByAttribute(
221                 $('used-source-fields-tabbox').getElementsByTagName('tab'),
222                 'selected',
223                 'true'
224         )[0];
225
226         var tabname = active_tab.getAttribute('id');
227
228         var tabpanel = $( tabname + 'panel' );
229         var tree = tabpanel.getElementsByTagName('tree')[0];
230         var item_pos = tree.view.selection.currentIndex;
231         var item =  getSelectedItems(tree)[0];
232         var relation_alias = item.getAttribute('relation');
233
234         var field = item.firstChild.firstChild;
235         var colname = field.nextSibling.getAttribute('label');
236
237         rpt_rel_cache[relation_alias].fields[tabname][colname].transform = trans;
238         rpt_rel_cache[relation_alias].fields[tabname][colname].aggregate = transform.aggregate;
239         rpt_rel_cache[relation_alias].fields[tabname][colname].params = transform.params;
240         rpt_rel_cache[relation_alias].fields[tabname][colname].transform_label = transform.label;
241
242         renderSources(true);
243         tree.view.selection.select( item_pos );
244         tree.focus();
245         tree.click();
246
247         $(tabname + '_trans_menu').hidePopup();
248         return true;
249 }
250
251 function changeOperator (args) {
252
253         var active_tab = filterByAttribute(
254                 $('used-source-fields-tabbox').getElementsByTagName('tab'),
255                 'selected',
256                 'true'
257         )[0];
258
259         var tabname = active_tab.getAttribute('id');
260
261         var tabpanel = $( tabname + 'panel' );
262         var tree = tabpanel.getElementsByTagName('tree')[0];
263         var item_pos = tree.view.selection.currentIndex;
264         var item = getSelectedItems(tree)[0];
265
266         var relation_alias = item.getAttribute('relation');
267
268         var field = item.firstChild.firstChild;
269         var colname = field.nextSibling.getAttribute('label');
270
271         rpt_rel_cache[relation_alias].fields[tabname][colname].op = args.op;
272         rpt_rel_cache[relation_alias].fields[tabname][colname].op_label = args.label;
273
274         renderSources(true);
275         tree.view.selection.select( item_pos );
276         tree.focus();
277         tree.click();
278
279         $(tabname + '_op_menu').hidePopup();
280         return true;
281 }
282
283 function removeTemplateFilterValue () {
284
285         var active_tab = filterByAttribute(
286                 $('used-source-fields-tabbox').getElementsByTagName('tab'),
287                 'selected',
288                 'true'
289         )[0];
290
291         var tabname = active_tab.getAttribute('id');
292
293         var tabpanel = $( tabname + 'panel' );
294         var tree = tabpanel.getElementsByTagName('tree')[0];
295         var item_pos = tree.view.selection.currentIndex;
296         var items = getSelectedItems(tree);
297
298         for (var i in items) {
299                 var item = items[i];
300                 var relation_alias = item.getAttribute('relation');
301
302                 var field = item.firstChild.firstChild;
303                 var colname = field.nextSibling.getAttribute('label');
304
305                 rpt_rel_cache[relation_alias].fields[tabname][colname].op_value = {};
306         }
307
308         renderSources(true);
309         tree.view.selection.select( item_pos );
310         return true;
311 }
312
313 function timestampSetDate (obj, cal, date) {
314         obj.op_value.value = date;
315         obj.op_value.object = cal.date;
316         obj.op_value.label = '"' + date + '"';
317
318         renderSources(true);
319         return true;
320 }
321
322 var __handler_cache;
323
324 function changeTemplateFilterValue () {
325
326         var active_tab = filterByAttribute(
327                 $('used-source-fields-tabbox').getElementsByTagName('tab'),
328                 'selected',
329                 'true'
330         )[0];
331
332         var tabname = active_tab.getAttribute('id');
333
334         var tabpanel = $( tabname + 'panel' );
335         var tree = tabpanel.getElementsByTagName('tree')[0];
336         var items = getSelectedItems(tree);
337
338         var targetCmd = $( tabname + '_value_action' );
339
340         targetCmd.menu = null;
341         targetCmd.command = null;
342         targetCmd.oncommand = null;
343         targetCmd.removeEventListener( 'command', __handler_cache, true );
344
345         for (var i in items) {
346                 var item = items[i];
347                 var relation_alias = item.getAttribute('relation');
348
349                 var field = item.firstChild.firstChild;
350                 var colname = field.nextSibling.getAttribute('label');
351
352                 var obj = rpt_rel_cache[relation_alias].fields[tabname][colname]
353                 var operation = OILS_RPT_FILTERS[obj.op];
354
355                 switch (obj.datatype) {
356                         case 'timestamp':
357                                 var cal_popup = $('calendar-widget');
358
359                                 while (cal_popup.firstChild) cal_popup.removeChild(cal_popup.lastChild);
360                                 var calendar = new Calendar(
361                                         0,
362                                         obj.op_value.object,
363                                         function (cal,date) { return timestampSetDate(obj,cal,date) },
364                                         function (cal) { cal_popup.hidePopup(); cal.destroy(); return true; }
365                                 );
366
367                                 var format = OILS_RPT_TRANSFORMS[obj.transform].cal_format || '%Y-%m-%d';
368
369                                 calendar.setDateFormat(format);
370                                 calendar.create(cal_popup);
371
372                                 targetCmd.menu = 'calendar-widget';
373
374                                 break;
375
376                         case 'bool':
377
378                                 function __bool_value_event_handler () {
379                                         var state, answer;
380
381                                         try {
382                                                 // get a reference to the prompt service component.
383                                                 var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
384                                                                     .getService(Components.interfaces.nsIPromptService);
385
386                                                 // set the buttons that will appear on the dialog. It should be
387                                                 // a set of constants multiplied by button position constants. In this case,
388                                                 // three buttons appear, Save, Cancel and a custom button.
389                                                 var flags=promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 +
390                                                         promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
391                                                         promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_2;
392
393                                                 // display the dialog box. The flags set above are passed
394                                                 // as the fourth argument. The next three arguments are custom labels used for
395                                                 // the buttons, which are used if BUTTON_TITLE_IS_STRING is assigned to a
396                                                 // particular button. The last two arguments are for an optional check box.
397                                                 answer = promptService.select(
398                                                         window,
399                                                         rpt_strings.TEMPLATE_CONF_BOOLEAN_VALUE,
400                                                         rpt_strings.TEMPLATE_CONF_SELECT_CANCEL,
401                                                         2, [rpt_strings.TEMPLATE_CONF_TRUE, rpt_strings.TEMPLATE_CONF_FALSE], state
402                                                 );
403                                         } catch (e) {
404                                                 answer = true;
405                                                 state = confirm(rpt_strings.TEMPLATE_CONF_CONFIRM_STATE);
406                                                 state ? state = 0 : state = 1;
407                                         }
408
409                                         if (answer) {
410                                                 if (state) {
411                                                         obj.op_value.value = 'f';
412                                                         obj.op_value.label = rpt_strings.TEMPLATE_CONF_FALSE;
413                                                 } else {
414                                                         obj.op_value.value = 't';
415                                                         obj.op_value.label = rpt_strings.TEMPLATE_CONF_TRUE;
416                                                 }
417                                         }
418
419                                         targetCmd.removeEventListener( 'command', __bool_value_event_handler, true );
420                                         renderSources(true);
421                                         tree.view.selection.select( item_pos );
422                                         return true;
423                                 }
424
425                                 __handler_cache = __bool_value_event_handler;
426                                 targetCmd.addEventListener( 'command', __bool_value_event_handler, true );
427
428                                 break;
429
430                         default:
431
432
433                                 var promptstring = rpt_strings.TEMPLATE_CONF_NO_MATCH;
434
435                                 switch (obj.op) {
436                                         case 'not between':
437                                                 promptstring = rpt_strings.TEMPLATE_CONF_NOT_BETWEEN;
438                                                 break;
439
440                                         case 'between':
441                                                 promptstring = rpt_strings.TEMPLATE_CONF_BETWEEN;
442                                                 break;
443
444                                         case 'not in':
445                                                 promptstring = rpt_strings.TEMPLATE_CONF_NOT_IN;
446                                                 break;
447
448                                         case 'in':
449                                                 promptstring = rpt_strings.TEMPLATE_CONF_IN;
450                                                 break;
451
452                                         default:
453                                                 promptstring =  dojo.string.substitute( rpt_strings.TEMPLATE_CONF_DEFAULT, [obj.op_label]);
454                                                 break;
455                                 }
456
457                                 function __default_value_event_handler () {
458                                         var _v;
459                                         if (_v  = prompt( promptstring, obj.op_value.value || '' )) {
460                                                 switch (obj.op) {
461                                                         case 'between':
462                                                         case 'not between':
463                                                         case 'not in':
464                                                         case 'in':
465                                                                 obj.op_value.value = _v.split(/\s*,\s*/);
466                                                                 break;
467
468                                                         default:
469                                                                 obj.op_value.value = _v;
470                                                                 break;
471                                                 }
472
473                                                 obj.op_value.label = '"' + obj.op_value.value + '"';
474                                         }
475
476                                         targetCmd.removeEventListener( 'command', __default_value_event_handler, true );
477                                         renderSources(true);
478                                         tree.view.selection.select( item_pos );
479                                         return true;
480                                 }
481
482                                 __handler_cache = __default_value_event_handler;
483                                 targetCmd.addEventListener( 'command', __default_value_event_handler, true );
484
485                                 break;
486                 }
487         }
488
489         return true;
490 }
491
492 function populateOperatorContext () {
493
494         var active_tab = filterByAttribute(
495                 $('used-source-fields-tabbox').getElementsByTagName('tab'),
496                 'selected',
497                 'true'
498         )[0];
499
500         var tabname = active_tab.getAttribute('id');
501
502         var tabpanel = $( tabname + 'panel' );
503         var tree = tabpanel.getElementsByTagName('tree')[0];
504         var items = getSelectedItems(tree);
505
506         var dtypes = [];
507         for (var i in items) {
508                 var item = items[i];
509                 dtypes.push( item.getAttribute('datatype') );
510         }
511
512         var menu = $(tabname + '_op_menu');
513         while (menu.firstChild) menu.removeChild(menu.lastChild);
514
515         for (var i in OILS_RPT_FILTERS) {
516                 var o = OILS_RPT_FILTERS[i];
517         if (o.label) {
518                 menu.appendChild(
519                         createMenuItem(
520                                 { label : o.label,
521                                   onmouseup : "changeOperator({op:'"+i+"',label:'"+o.label+"'})"
522                                     }
523                         )
524                 );
525         }
526
527                 if (o.labels) {
528                         var keys = getKeys(o.labels);
529                         for ( var k in keys ) {
530                                 var key = keys[k];
531                                 if ( grep(function(x){return key==x},dtypes).length ) {
532                                         menu.appendChild(
533                                                 createMenuItem(
534                                                         { label : o.labels[key],
535                                                           onmouseup : "changeOperator({op:'"+i+"',label:'"+o.labels[key]+"'});"
536                                                         }
537                                                 )
538                                         );
539                                 }
540                         }
541                 }
542         }
543 }
544
545 function populateTransformContext () {
546
547         var active_tab = filterByAttribute(
548                 $('used-source-fields-tabbox').getElementsByTagName('tab'),
549                 'selected',
550                 'true'
551         )[0];
552
553         var tabname = active_tab.getAttribute('id');
554
555         var tabpanel = $( tabname + 'panel' );
556         var tree = tabpanel.getElementsByTagName('tree')[0];
557         var items = getSelectedItems(tree);
558
559         var transforms = {};
560         for (var i in items) {
561                 var item = items[i];
562                 var dtype = item.getAttribute('datatype');
563                 var item_transforms = getTransforms({ datatype : dtype });
564
565                 for (var j in item_transforms) {
566                         transforms[item_transforms[j]] = OILS_RPT_TRANSFORMS[item_transforms[j]];
567                         transforms[item_transforms[j]].name = item_transforms[j];
568                 }
569         }
570
571         var transformList = [];
572         for (var i in transforms) {
573                 transformList.push( transforms[i] );
574         }
575
576         transformList.sort( sortHashLabels );
577
578         var menu = $(tabname + '_trans_menu');
579         while (menu.firstChild) menu.removeChild(menu.lastChild);
580
581         for (var i in transformList) {
582                 var t = transformList[i];
583
584                 if (tabname.match(/filter/)) {
585                         if (tabname.match(/^agg/)) {
586                                 if (!t.aggregate) continue;
587                         } else {
588                                 if (t.aggregate) continue;
589                         }
590                 }
591
592                 menu.appendChild(
593                         createMenuItem(
594                                 { aggregate : t.aggregate,
595                                   name : t.name,
596                                   alias : t.label,
597                                   label : t.label,
598                                   params : t.params,
599                                   onmouseup : "alterColumnTransform('"+t.name+"')"
600                                 }
601                         )
602                 );
603         }
604
605         menu.parentNode.setAttribute('disabled','false');
606         if (menu.childNodes.length == 0) {
607                 menu.parentNode.setAttribute('disabled','true');
608         }
609 }
610
611
612 function renderSources (selected) {
613
614         var tree = $('used-sources');
615         var sources = $('used-sources-treetop');
616         var tabs = $('used-source-fields-tabbox').getElementsByTagName('tab');
617
618         if (!selected) {
619                 while (sources.firstChild) sources.removeChild(sources.lastChild);
620         } else {
621                 selected = getSelectedItems(tree);
622                 if (!selected.length) {
623                         selected = undefined;
624                         while (sources.firstChild) sources.removeChild(sources.lastChild);
625                 }
626         }
627
628         for (var j = 0; j < tabs.length; j++) {
629                 var tab = tabs[j];
630                 var tabname = tab.getAttribute('id')
631                 var tabpanel = $( tab.getAttribute('id') + 'panel' );
632                 var fieldtree = tabpanel.getElementsByTagName('treechildren')[0];
633
634                 while (fieldtree.firstChild) fieldtree.removeChild(fieldtree.lastChild);
635         }
636
637         for (var relation_alias in rpt_rel_cache) {
638                 if (!rpt_rel_cache[relation_alias].fields) continue;
639
640                 if (selected) {
641                         if (
642                                 !grep(
643                                         function (x) {
644                                                 return x.getAttribute('relation') == relation_alias;
645                                         },
646                                         selected
647                                 ).length
648                         ) continue;
649                 } else {
650
651                         $('used-sources-treetop').appendChild(
652                                 createTreeItem(
653                                         { relation : rpt_rel_cache[relation_alias].alias,
654                                           idlclass : rpt_rel_cache[relation_alias].idlclass,
655                                           reltype  : rpt_rel_cache[relation_alias].reltype,
656                                           path     : rpt_rel_cache[relation_alias].path
657                                         },
658                                         createTreeRow(
659                                                 {},
660                                                 createTreeCell({ label : rpt_rel_cache[relation_alias].label }),
661                                                 createTreeCell({ label : rpt_rel_cache[relation_alias].table }),
662                                                 createTreeCell({ label : rpt_rel_cache[relation_alias].alias }),
663                                                 createTreeCell({ label : rpt_rel_cache[relation_alias].reltype })
664                                         )
665                                 )
666                         );
667                 }
668
669                 for each (var tabname in ['filter_tab','aggfilter_tab']) {
670                         tabpanel = $( tabname + 'panel' );
671                         fieldtree = tabpanel.getElementsByTagName('treechildren')[0];
672
673                         for (var colname in rpt_rel_cache[relation_alias].fields[tabname]) {
674                                 with (rpt_rel_cache[relation_alias].fields[tabname][colname]) {
675                                         fieldtree.appendChild(
676                                                 createTreeItem(
677                                                         { relation : relation_alias, datatype : datatype },
678                                                         createTreeRow(
679                                                                 {},
680                                                                 createTreeCell({ label : alias }),
681                                                                 createTreeCell({ label : colname }),
682                                                                 createTreeCell({ label : datatype }),
683                                                                 createTreeCell({ label : transform_label }),
684                                                                 createTreeCell({ label : transform })
685                                                         )
686                                                 )
687                                         );
688
689                                         fieldtree.lastChild.firstChild.appendChild(
690                                                 createTreeCell({ op : op, label : op_label })
691                                         );
692
693                                         if (op_value.value != undefined) {
694                                                 fieldtree.lastChild.firstChild.appendChild(
695                                                         createTreeCell({ label : op_value.label })
696                                                 );
697                                         }
698                                 }
699                         }
700                 }
701         }
702
703         tabpanel = $( 'dis_tabpanel' );
704         fieldtree = tabpanel.getElementsByTagName('treechildren')[0];
705         for each (var order in rpt_rel_cache.order_by) {
706
707                 if (selected) {
708                         if (
709                                 !grep(
710                                         function (x) {
711                                                 return x.getAttribute('relation') == order.relation;
712                                         },
713                                         selected
714                                 ).length
715                         ) continue;
716                 }
717
718                 with (rpt_rel_cache[order.relation].fields.dis_tab[order.field]) {
719                         fieldtree.appendChild(
720                                 createTreeItem(
721                                         { relation : order.relation, datatype : datatype },
722                                         createTreeRow(
723                                                 {},
724                                                 createTreeCell({ label : alias }),
725                                                 createTreeCell({ label : colname }),
726                                                 createTreeCell({ label : datatype }),
727                                                 createTreeCell({ label : transform_label }),
728                                                 createTreeCell({ label : transform })
729                                         )
730                                 )
731                         );
732
733                         fieldtree.lastChild.firstChild.appendChild(
734                                 createTreeCell({ op : op, label : op_label })
735                         );
736
737                         if (op_value.value != undefined) {
738                                 fieldtree.lastChild.firstChild.appendChild(
739                                         createTreeCell({ label : op_value.label })
740                                 );
741                         }
742                 }
743         }
744 }
745
746 var param_count;
747 var tab_use = {
748         dis_tab       : 'select',
749         filter_tab    : 'where',
750         aggfilter_tab : 'having',
751 };
752
753
754 function save_template () {
755         param_count = 0;
756
757         var template = {
758                 version    : 3,
759                 core_class : $('sources-treetop').getElementsByTagName('treeitem')[0].getAttribute('idlclass'),
760                 select     : [],
761                 from       : {},
762                 where      : [],
763                 having     : [],
764                 order_by   : []
765         };
766
767         for (var relname in rpt_rel_cache) {
768                 var relation = rpt_rel_cache[relname];
769                 if (!relation.fields) continue;
770
771                 // first, add the where and having clauses (easier)
772                 for each (var tab_name in [ 'filter_tab', 'aggfilter_tab' ]) {
773                         var tab = relation.fields[tab_name];
774                         for (var field in tab) {
775                                 fleshTemplateField( template, relation, tab_name, field );
776                         }
777                 }
778
779                 // now the from clause
780                 fleshFromPath( template, relation );
781         }
782
783         // and now for select (based on order_by)
784         for each (var order in rpt_rel_cache.order_by)
785                 fleshTemplateField( template, rpt_rel_cache[order.relation], 'dis_tab', order.field );
786
787         template.rel_cache = rpt_rel_cache;
788
789         //prompt( 'template', js2JSON( template ) );
790
791         // and the saving throw ...
792         var cgi = new CGI();
793         var session = cgi.param('ses');
794         fetchUser( session );
795
796         var tmpl = new rt();
797         tmpl.name( $('template-name').value );
798         tmpl.description( $('template-description').value );
799         tmpl.owner(USER.id());
800         tmpl.folder(cgi.param('folder'));
801         tmpl.data(js2JSON(template));
802
803         if(!confirm(dojo.string.substitute( rpt_strings.TEMPLATE_CONF_CONFIRM_SAVE, [tmpl.name(), tmpl.description()] )))
804                 return;
805
806         var req = new Request('open-ils.reporter:open-ils.reporter.template.create', session, tmpl);
807         req.request.alertEvent = false;
808         req.callback(
809                 function(r) {
810                         var res = r.getResultObject();
811                         if(checkILSEvent(res)) {
812                                 alertILSEvent(res);
813                         } else {
814                                 if( res && res != '0' ) {
815                                         alert(dojo.string.substitute( rpt_strings.TEMPLATE_CONF_SUCCESS_SAVE, [tmpl.name()] ));
816                                         _l('../oils_rpt.xhtml');
817                                 }
818                         }
819                 }
820         );
821
822         req.send();
823 }
824
825 function fleshFromPath ( template, rel ) {
826         var table_path = rel.path.split( /\./ );
827         if (table_path.length > 1 || rel.path.indexOf('-') > -1) table_path.push( rel.idlclass );
828
829         var prev_type = '';
830         var prev_link = '';
831         var current_path = '';
832         var current_obj = template.from;
833         var link;
834         while (link = table_path.shift()) {
835
836                 if (prev_link != '') {
837                         var prev_class = getIDLClass( prev_link.split(/-/)[0] );
838                         var prev_field = prev_link.split(/-/)[1];
839
840                         var prev_join = prev_field;
841
842                         prev_field = prev_field.split(/>/)[0];
843                         prev_join = prev_join.split(/>/)[1];
844
845                         var current_link = getIDLLink( prev_class, prev_field );
846                         current_obj.key = current_link.getAttribute('key');
847
848             //console.log("prev_link in fleshFromPath is: " + prev_link);
849             //console.log("prev_join in fleshFromPath is: " + prev_join);
850
851             if (prev_join) current_obj.type = prev_join
852                         else if ( 
853                                 (
854                                         current_link.getAttribute('reltype') != 'has_a' ||
855                                         prev_type == 'left' ||
856                                         rel.reltype != 'has_a'
857
858 // This disallows outer joins when the item is used in a filter
859 //                              ) && (
860 //                                      getKeys(rel.fields.filter_tab).length == 0 &&
861 //                                      getKeys(rel.fields.aggfitler_tab).length == 0
862
863                                 )
864                         ) current_obj.type = 'left';
865
866                         prev_type = current_obj.type; 
867
868                 }
869
870                 if (current_path) current_path += '-';
871                 current_path += link.split(/>/)[0];
872
873                 var leaf = table_path.length == 0 ? true : false;
874
875                 current_obj.path = current_path;
876                 current_obj.table = getSourceDefinition( link.split(/-/)[0] );
877
878
879                 if (leaf) {
880
881                 var join_type = link.split(/-/)[1];
882             if (join_type) {
883                         join_type = join_type.split(/>/)[1];
884                             if (join_type && join_type != 'undefined') current_obj.type = join_type;
885             }
886
887                         current_obj.label = rel.label;
888                         current_obj.alias = rel.alias;
889                         current_obj.idlclass = rel.idlclass;
890                         current_obj.template_path = rel.path;
891                 } else {
892                         var current_class = getIDLClass( link.split(/-/)[0] );
893                         var join_field = link.split(/-/)[1];
894                         var join_type = join_field;
895
896                         join_field = join_field.split(/>/)[0];
897                         join_type = join_type.split(/>/)[1];
898
899             //console.log("join_field in fleshFromPath is: " + join_field);
900
901                         var join_link = getIDLLink(current_class, join_field);
902
903                         if (join_link.getAttribute('reltype') != 'has_a') {
904                                 var fields_el = current_class.getElementsByTagName('fields')[0];
905                                 join_field =
906                                         fields_el.getAttributeNS(persistNS, 'primary') +
907                                         '-' + join_link.getAttribute('class') +
908                                         '-' + join_link.getAttribute('key');
909                         }
910
911                         if (!current_obj.alias) current_obj.alias = hex_md5( current_path ) ;
912                         join_field += '-' + current_obj.alias;
913
914                         if (!current_obj.join) current_obj.join = {};
915                         if (!current_obj.join[join_field]) current_obj.join[join_field] = {};
916
917                         if (current_obj.type == 'left') current_obj.join[join_field].type = 'left';
918
919                         current_obj = current_obj.join[join_field];
920                 }
921
922                 prev_link = link;
923         }
924
925
926 }
927
928 function fleshTemplateField ( template, rel, tab_name, field ) {
929
930         if (!rel.fields[tab_name] || !rel.fields[tab_name][field]) return;
931
932         var tab = rel.fields[tab_name];
933
934         var table_path = rel.path.split( /\./ );
935         if (table_path.length > 1 || rel.path.indexOf('-') > -1)
936                 table_path.push( rel.idlclass );
937
938         table_path.push( field );
939
940         var field_path = table_path.join('-');
941
942         var element = {
943                 alias : tab[field].alias,
944                 column :
945                         { colname : field,
946                           transform : tab[field].transform,
947                           transform_label : tab[field].transform_label
948                         },
949                 path : field_path,
950                 relation : rel.alias
951         };
952
953         if (tab_name.match(/filter/)) {
954                 element.condition = {};
955                 if (tab[field].op == 'is' || tab[field].op == 'is not' || tab[field].op == 'is blank' || tab[field].op == 'is not blank') {
956                         element.condition[tab[field].op] = null;
957                 } else {
958                         element.condition[tab[field].op] =
959                                 tab[field].op_value.value ?
960                                         tab[field].op_value.value :
961                                         '::P' + param_count++;
962                 }
963         }
964
965         template[tab_use[tab_name]].push(element);
966 }
967