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