From a4e66081fcd46f49db0663c37b9adf367b156d0f Mon Sep 17 00:00:00 2001 From: "(no author)" <(no author)@dcc99617-32d9-48b4-a31d-7c20da2025e4> Date: Mon, 14 May 2007 02:46:12 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create branch 'rel_1_0'. git-svn-id: svn://svn.open-ils.org/ILS/branches/rel_1_0@7302 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/src/sql/Pg/example.reporter-extension.sql | 122 +++ Open-ILS/web/reports/xul/operators.js | 60 ++ Open-ILS/web/reports/xul/reporter.css | 0 Open-ILS/web/reports/xul/source-browse.js | 190 +++++ Open-ILS/web/reports/xul/source-setup.js | 300 +++++++ Open-ILS/web/reports/xul/template-config.js | 919 +++++++++++++++++++++ Open-ILS/web/reports/xul/template_builder.xul | 315 +++++++ Open-ILS/web/reports/xul/transforms.js | 233 ++++++ Open-ILS/web/reports/xul/utilities.js | 83 ++ Open-ILS/web/reports/xul/xulbuilder.js | 107 +++ 10 files changed, 2329 insertions(+) create mode 100644 Open-ILS/src/sql/Pg/example.reporter-extension.sql create mode 100644 Open-ILS/web/reports/xul/operators.js create mode 100644 Open-ILS/web/reports/xul/reporter.css create mode 100644 Open-ILS/web/reports/xul/source-browse.js create mode 100644 Open-ILS/web/reports/xul/source-setup.js create mode 100644 Open-ILS/web/reports/xul/template-config.js create mode 100644 Open-ILS/web/reports/xul/template_builder.xul create mode 100644 Open-ILS/web/reports/xul/transforms.js create mode 100644 Open-ILS/web/reports/xul/utilities.js create mode 100644 Open-ILS/web/reports/xul/xulbuilder.js diff --git a/Open-ILS/src/sql/Pg/example.reporter-extension.sql b/Open-ILS/src/sql/Pg/example.reporter-extension.sql new file mode 100644 index 0000000000..6d1e375c30 --- /dev/null +++ b/Open-ILS/src/sql/Pg/example.reporter-extension.sql @@ -0,0 +1,122 @@ +BEGIN; + +CREATE OR REPLACE VIEW reporter.classic_current_circ AS +SELECT cl.shortname AS circ_lib, + cl.id AS circ_lib_id, + circ.xact_start AS xact_start, + circ_type.type AS circ_type, + cp.id AS copy_id, + cp.circ_modifier, + ol.shortname AS owning_lib_name, + lm.value AS language, + lfm.value AS lit_form, + ifm.value AS item_form, + itm.value AS item_type, + sl.name AS shelving_location, + p.id AS patron_id, + g.name AS profile_group, + dem.general_division AS demographic_general_division, + circ.id AS id, + cn.id AS call_number, + cn.label AS call_number_label, + call_number_dewey(cn.label) AS dewey, + CASE + WHEN call_number_dewey(cn.label) ~ E'^[0-9.]+$' + THEN + btrim( + to_char( + 10 * floor((call_number_dewey(cn.label)::float) / 10), '000' + ) + ) + ELSE NULL + END AS dewey_block_tens, + CASE + WHEN call_number_dewey(cn.label) ~ E'^[0-9.]+$' + THEN + btrim( + to_char( + 100 * floor((call_number_dewey(cn.label)::float) / 100), '000' + ) + ) + ELSE NULL + END AS dewey_block_hundreds, + CASE + WHEN call_number_dewey(cn.label) ~ E'^[0-9.]+$' + THEN + btrim( + to_char( + 10 * floor((call_number_dewey(cn.label)::float) / 10), '000' + ) + ) + || '-' || + btrim( + to_char( + 10 * floor((call_number_dewey(cn.label)::float) / 10) + 9, '000' + ) + ) + ELSE NULL + END AS dewey_range_tens, + CASE + WHEN call_number_dewey(cn.label) ~ E'^[0-9.]+$' + THEN + btrim( + to_char( + 100 * floor((call_number_dewey(cn.label)::float) / 100), '000' + ) + ) + || '-' || + btrim( + to_char( + 100 * floor((call_number_dewey(cn.label)::float) / 100) + 99, '000' + ) + ) + ELSE NULL + END AS dewey_range_hundreds, + hl.id AS patron_home_lib, + hl.shortname AS patron_home_lib_shortname, + paddr.county AS patron_county, + paddr.city AS patron_city, + paddr.post_code AS patron_zip, + sc1.stat_cat_entry AS stat_cat_1, + sc2.stat_cat_entry AS stat_cat_2, + sce1.value AS stat_cat_1_value, + sce2.value AS stat_cat_2_value + FROM action.circulation circ + JOIN reporter.circ_type circ_type ON (circ.id = circ_type.id) + JOIN asset.copy cp ON (cp.id = circ.target_copy) + JOIN asset.copy_location sl ON (cp.location = sl.id) + JOIN asset.call_number cn ON (cp.call_number = cn.id) + JOIN actor.org_unit ol ON (cn.owning_lib = ol.id) + JOIN metabib.rec_descriptor rd ON (rd.record = cn.record) + JOIN actor.org_unit cl ON (circ.circ_lib = cl.id) + JOIN actor.usr p ON (p.id = circ.usr) + JOIN actor.org_unit hl ON (p.home_ou = hl.id) + JOIN permission.grp_tree g ON (p.profile = g.id) + JOIN reporter.demographic dem ON (dem.id = p.id) + JOIN actor.usr_address paddr ON (paddr.id = p.billing_address) + LEFT JOIN config.language_map lm ON (rd.item_lang = lm.code) + LEFT JOIN config.lit_form_map lfm ON (rd.lit_form = lfm.code) + LEFT JOIN config.item_form_map ifm ON (rd.item_form = ifm.code) + LEFT JOIN config.item_type_map itm ON (rd.item_type = itm.code) + LEFT JOIN asset.stat_cat_entry_copy_map sc1 ON (sc1.owning_copy = cp.id AND sc1.stat_cat = 1) + LEFT JOIN asset.stat_cat_entry sce1 ON (sce1.id = sc1.stat_cat_entry) + LEFT JOIN asset.stat_cat_entry_copy_map sc2 ON (sc2.owning_copy = cp.id AND sc2.stat_cat = 2) + LEFT JOIN asset.stat_cat_entry sce2 ON (sce2.id = sc2.stat_cat_entry); + +CREATE OR REPLACE VIEW reporter.legacy_cat1 AS +SELECT id, + owner, + value + FROM asset.stat_cat_entry + WHERE stat_cat = 1; + +CREATE OR REPLACE VIEW reporter.legacy_cat2 AS +SELECT id, + owner, + value + FROM asset.stat_cat_entry + WHERE stat_cat = 2; + + +COMMIT; + diff --git a/Open-ILS/web/reports/xul/operators.js b/Open-ILS/web/reports/xul/operators.js new file mode 100644 index 0000000000..81982fd569 --- /dev/null +++ b/Open-ILS/web/reports/xul/operators.js @@ -0,0 +1,60 @@ + +var OILS_RPT_FILTERS = { + '=' : { + label : 'Equals', + }, + + 'like' : { + label : 'Contains Matching substring', + }, + + ilike : { + label : 'Contains Matching substring (ignore case)', + }, + + '>' : { + label : 'Greater than', + labels : { timestamp : 'After (Date/Time)' } + }, + + '>=' : { + label : 'Greater than or equal to', + labels : { timestamp : 'On or After (Date/Time)' } + }, + + + '<' : { + label : 'Less than', + labels : { timestamp : 'Before (Date/Time)' } + }, + + '<=' : { + label : 'Less than or equal to', + labels : { timestamp : 'On or Before (Date/Time)' } + }, + + 'in' : { + label : 'In list', + }, + + 'not in' : { + label : 'Not in list', + }, + + 'between' : { + label : 'Between', + }, + + 'not between' : { + label : 'Not between', + }, + + 'is' : { + label : 'Is NULL' + }, + + 'is not' : { + label : 'Is not NULL' + } +} + diff --git a/Open-ILS/web/reports/xul/reporter.css b/Open-ILS/web/reports/xul/reporter.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Open-ILS/web/reports/xul/source-browse.js b/Open-ILS/web/reports/xul/source-browse.js new file mode 100644 index 0000000000..5bc9302b57 --- /dev/null +++ b/Open-ILS/web/reports/xul/source-browse.js @@ -0,0 +1,190 @@ + +function sourceTreeHandlerDblClick (ev) { return sourceTreeHandler(ev,true) } + +function sourceTreeHandler (ev, dbl) { + + var row = {}, col = {}, part = {}, item; + var tree = $('idl-browse-tree'); + + + try { + tree.treeBoxObject.getCellAt(ev.clientX, ev.clientY, row, col, part); + item = tree.contentView.getItemAtIndex(row.value); + } catch (e) { + // ... meh + } + + if (part.value == 'twisty' || dbl) { // opening or closing + if (item.getAttribute('open') == 'true' && item.lastChild.childNodes.length == 0) { + //var subtree = item.lastChild; + //while (subtree.childNodes.length) + // subtree.removeChild(subtree.lastChild); + + var p = getIDLClass( item.getAttribute('idlclass') ); + + var subtreeList = []; + var link_fields = p.getElementsByTagName('link'); + + for ( var i = 0; i < link_fields.length; i++ ) { + var field = getIDLField( p, link_fields[i].getAttribute('field') ); + + if (!field) continue; + + var name = field.getAttributeNS(rptNS,'label'); + if (!name) name = field.getAttribute('name'); + + var idlclass = link_fields[i].getAttribute('class'); + var map = link_fields[i].getAttribute('map'); + var link = link_fields[i].getAttribute('field'); + var key = link_fields[i].getAttribute('key'); + var reltype = link_fields[i].getAttribute('reltype'); + + if (map) continue; + + var pathList = []; + findAnscestorStack( item, 'treeitem', pathList ); + + var fullpath = ''; + for (var j in pathList.reverse()) { + var n = pathList[j].getAttribute('idlclass'); + var f = pathList[j].getAttribute('field'); + + if (f) fullpath += "-" + f; + + if (fullpath) fullpath += "."; + fullpath += n; + + } + + fullpath += "-" + link; + + subtreeList.push( + { name : name, + idlclass : idlclass, + map : map, + key : key, + field : field.getAttribute('name'), + reltype : reltype, + link : link, + fullpath : fullpath + } + ); + } + + populateSourcesSubtree( item.lastChild, subtreeList ); + } + } else if (item) { + var classtree = $('class-treetop'); + + while (classtree.childNodes.length) + classtree.removeChild(classtree.lastChild); + + var c = getIDLClass( item.getAttribute('idlclass') ); + + populateDetailTree( + classtree, + c, + item + ); + } + + return true; +} + +function transformSelectHandler (noswap) { + var transform_tree = $('trans-view'); + var transform = getSelectedItems(transform_tree)[0]; + + if (transform) { + if (transform.getAttribute('aggregate') == 'true') { + $( 'filter_tab' ).setAttribute('disabled','true'); + $( 'aggfilter_tab' ).setAttribute('disabled','false'); + + if (!noswap && $( 'filter_tab' ).selected) + $( 'filter_tab' ).parentNode.selectedItem = $( 'aggfilter_tab' ); + } else { + $( 'filter_tab' ).setAttribute('disabled','false'); + $( 'aggfilter_tab' ).setAttribute('disabled','true'); + + if (!noswap && $( 'aggfilter_tab' ).selected) + $( 'aggfilter_tab' ).parentNode.selectedItem = $( 'filter_tab' ); + } + } + + if ($( 'filter_tab' ).selected) { + if ($( 'filter_tab' ).getAttribute('disabled') == 'true') + $( 'source-add' ).setAttribute('disabled','true'); + else + $( 'source-add' ).setAttribute('disabled','false'); + + } else if ($( 'aggfilter_tab' ).selected) { + if ($( 'aggfilter_tab' ).getAttribute('disabled') == 'true') + $( 'source-add' ).setAttribute('disabled','true'); + else + $( 'source-add' ).setAttribute('disabled','false'); + + } else if ($( 'dis_tab' ).selected) { + $( 'source-add' ).setAttribute('disabled','false'); + } +} + +function detailTreeHandler (args) { + var class_tree = $('class-view'); + var transform_tree = $('trans-treetop'); + + while (transform_tree.childNodes.length) + transform_tree.removeChild(transform_tree.lastChild); + + var class_items = getSelectedItems(class_tree);; + + var transforms = new Object(); + for (var i in class_items) { + var item = class_items[i]; + var dtype = item.lastChild.lastChild.getAttribute('label'); + + var item_transforms = getTransforms({ datatype : dtype }); + + for (var j in item_transforms) { + transforms[item_transforms[j]] = OILS_RPT_TRANSFORMS[item_transforms[j]]; + transforms[item_transforms[j]].name = item_transforms[j]; + } + } + + var transformList = []; + for (var i in transforms) { + transformList.push( transforms[i] ); + } + + transformList.sort( sortHashLabels ); + + $( 'aggfilter_tab' ).setAttribute('disabled','true'); + + for (var i in transformList) { + var t = transformList[i]; + transform_tree.appendChild( + createTreeItem( + { aggregate : t.aggregate, + name : t.name, + alias : t.label, + params : t.params, + }, + createTreeRow( + {}, + createTreeCell( { label : t.label } ), + createTreeCell( { label : t.params ? t.params : '0' } ), + createTreeCell( { label : t.datatype.length > 0 ? t.datatype.join(', ') : 'all' } ), + createTreeCell( { label : t.aggregate ? 'Aggregate' : 'Non-Aggregate' } ) + ) + ) + ); + + if (t.aggregate) $( 'aggfilter_tab' ).setAttribute('disabled','false'); + } + + transformSelectHandler(true); + + return true; +} + + + diff --git a/Open-ILS/web/reports/xul/source-setup.js b/Open-ILS/web/reports/xul/source-setup.js new file mode 100644 index 0000000000..4cf5222329 --- /dev/null +++ b/Open-ILS/web/reports/xul/source-setup.js @@ -0,0 +1,300 @@ + +var idlNS = "http://opensrf.org/spec/IDL/base/v1"; +var persistNS = "http://open-ils.org/spec/opensrf/IDL/persistance/v1"; +var objNS = "http://open-ils.org/spec/opensrf/IDL/objects/v1"; +var rptNS = "http://open-ils.org/spec/opensrf/IDL/reporter/v1"; +var gwNS = "http://opensrf.org/-/namespaces/gateway/v1"; +var xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + +var oilsIDL; +var rpt_rel_cache = {}; + +function sortHashLabels (a,b) { return a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1; } + +function sortNames (a,b) { + var aname = a.name.toLowerCase(); + if (!aname) aname = a.idlclass.toLowerCase(); + + var bname = b.name.toLowerCase(); + if (!bname) bname = b.idlclass.toLowerCase(); + + return aname < bname ? -1 : 1; +} + +function sortLabels (a,b) { + var aname = a.getAttributeNS(rptNS, 'label').toLowerCase(); + if (!aname) aname = a.getAttribute('name'); + if (!aname) aname = a.getAttribute('id'); + + var bname = b.getAttributeNS(rptNS, 'label').toLowerCase(); + if (!bname) bname = b.getAttribute('name'); + if (!bname) bname = b.getAttribute('id'); + + return aname < bname ? -1 : 1; +} + + +function loadTemplate(id) { + var cgi = new CGI(); + var session = cgi.param('ses'); + + var r = new Request('open-ils.reporter:open-ils.reporter.template.retrieve', session, id); + + r.callback( + function(res) { + var tmpl = res.getResultObject(); + var template = JSON2js( tmpl.data() ); + + resetUI( template.core_class ); + + $('template-name').value = tmpl.name() + ' (clone)'; + $('template-description').value = tmpl.description(); + + rpt_rel_cache = template.rel_cache; + renderSources(); + } + ); + + r.send(); +} + + +function loadIDL() { + var req = new XMLHttpRequest(); + req.open('GET', '../fm_IDL.xml', true); + req.onreadystatechange = function() { + if( req.readyState == 4 ) { + oilsIDL = req.responseXML; + populateSourcesMenu( + filterByAttributeNS( oilsIDL.getElementsByTagName('class'), rptNS, 'core', 'true' ) + ); + + var cgi = new CGI(); + var template_id = cgi.param('ct'); + if (template_id) loadTemplate(template_id); + } + } + req.send(null); + +} + +function getIDLClass (id) { return filterByAttribute( oilsIDL.getElementsByTagName('class'), 'id', id )[0] } +function getIDLField (classNode,field) { return filterByAttribute( classNode.getElementsByTagName('field'), 'name', field )[0] } +function getIDLLink (classNode,field) { return filterByAttribute( classNode.getElementsByTagName('link'), 'field', field )[0] } + +function resetUI (idlclass) { + if (getKeys(rpt_rel_cache).length > 0) { + if (!confirm( + "You have started building a template!\n" + + "Selecting a new starting source will destroy " + + "the current template and start over. Is this OK?" + )) return false; + } + + rpt_rel_cache = {}; + try { renderSources(); } catch (e) {} + + populateSourcesTree( idlclass ); + + var tree = $('sources-treetop').parentNode; + tree.focus(); + tree.view.selection.select(0); + tree.click(); +} + +function populateSourcesMenu (classList) { + classList.sort( sortLabels ); + + var menu = $('source-menu'); + + menu.appendChild( + createMenuItem( + { label : 'Core Sources', + disabled : 'true', + style : 'color: black; text-decoration: underline;' + } + ) + ); + + + for (var i in classList) { + + var name = classList[i].getAttributeNS(rptNS,'label'); + var id = classList[i].getAttribute('id'); + if (!name) name = id; + + menu.appendChild( + createMenuItem( + { container : 'true', + idlclass : id, + label : name, + onmouseup : 'resetUI( "' + id + '");' + } + ) + ); + + } + + menu.appendChild( createMenuSeparator() ); + + var _m = createMenu( + { label : 'All Available Sources' }, + createMenuPopup( + {}, + createMenuItem( + { label : 'All Available Sources', + disabled : 'true', + style : 'color: black; ' + } + ), + createMenuSeparator() + ) + ); + + menu.appendChild( _m ); + menu = _m.firstChild; + + var all = map(function(x){return x;}, oilsIDL.getElementsByTagNameNS(idlNS,'class')); + all.sort( sortLabels ); + + for (var i = 0; i < all.length; i++) { + + if (all[i].getAttributeNS(persistNS,'virtual') == 'true') continue; + + var name = all[i].getAttributeNS(rptNS,'label'); + var id = all[i].getAttribute('id'); + if (!name) name = id; + + menu.appendChild( + createMenuItem( + { container : 'true', + idlclass : id, + label : name, + onmouseup : 'resetUI( "' + id + '");' + } + ) + ); + + } + + +} + +function populateSourcesTree (idlclass) { + + var tcNode = $('sources-treetop'); + while (tcNode.childNodes.length) tcNode.removeChild(tcNode.lastChild); + + var c = getIDLClass( idlclass ); + var name = c.getAttributeNS(rptNS,'label'); + if (!name) name = idlclass; + + tcNode.appendChild( + createTreeItem( + { container : 'true', + idlclass : idlclass, + fullpath : idlclass + }, + createTreeRow( + { }, + createTreeCell( { label : name } ) + ), + createTreeChildren( { alternatingbackground : true } ) + ) + ); +} + +function populateSourcesSubtree (tcNode, classList) { + classList.sort(sortNames); + for (var i in classList) { + var obj = classList[i]; + + var p = getIDLClass( obj.idlclass ); + var cont = p.getElementsByTagName('link').length ? 'true' : 'false'; + + tcNode.appendChild( + createTreeItem( + { container : cont, + idlclass : obj.idlclass, + map : obj.map, + key : obj.key, + field : obj.field, + link : obj.link, + reltype : obj.reltype, + fullpath : obj.fullpath, + }, + createTreeRow( + { }, + createTreeCell( { label : obj.name } ) + ), + createTreeChildren( { alternatingbackground : true } ) + ) + ); + + + } +} + +function populateDetailTree (tcNode, c, item) { + var fullpath = item.getAttribute('fullpath'); + var reltype = item.getAttribute('reltype'); + + var fields = filterByAttributeNS(c.getElementsByTagName('field'),persistNS, 'virtual','false'); + fields.sort( sortLabels ); + + var id = c.getAttribute('id'); + var path_label = []; + + var steps = fullpath.split('.'); + for (var k in steps) { + + if (!steps[k]) continue; + + var atom = steps[k].split('-'); + var classNode = getIDLClass(atom[0]); + + var _cname = classNode.getAttributeNS(rptNS, 'label'); + if (!_cname) _cname = classNode.getAttribute('id'); + + var _label = _cname; + + if (atom.length > 1 && k == steps.length - 1) { + var _f = getIDLField(classNode, atom[1]); + var _fname = _f.getAttributeNS(rptNS, 'label'); + if (!_fname) _fname = _f.getAttribute('name'); + if (_fname) _label += ' :: ' + _fname; + } + + path_label.push(_label); + } + + $('path-label').value = path_label.join(' -> '); + $('path-label').setAttribute('reltype',reltype); + + for (var i in fields) { + + var type = fields[i].getAttributeNS(rptNS, 'datatype'); + //if (!type) type = 'text'; + + var label = fields[i].getAttributeNS(rptNS, 'label'); + var name = fields[i].getAttribute('name'); + if (!label) label = name; + + tcNode.appendChild( + createTreeItem( + { idlclass : id, + idlfield : name, + datatype : type, + fullpath : fullpath + }, + createTreeRow( + { }, + createTreeCell( { label : label } ), + createTreeCell( { label : type } ) + ) + ) + ); + } +} + + diff --git a/Open-ILS/web/reports/xul/template-config.js b/Open-ILS/web/reports/xul/template-config.js new file mode 100644 index 0000000000..c1d533dde9 --- /dev/null +++ b/Open-ILS/web/reports/xul/template-config.js @@ -0,0 +1,919 @@ +function removeReportAtom (args) { + if (!args) args = {}; + + var active_tab = filterByAttribute( + $('used-source-fields-tabbox').getElementsByTagName('tab'), + 'selected', + 'true' + )[0]; + var tabname = active_tab.getAttribute('id'); + + var tabpanel = $( tabname + 'panel' ); + var tree = tabpanel.getElementsByTagName('tree')[0]; + var fields = getSelectedItems(tree); + + + for (var i in fields) { + var field = fields[i]; + var colname = field.firstChild.firstChild.nextSibling.getAttribute('label'); + var relation_alias = field.getAttribute('relation'); + + delete rpt_rel_cache[relation_alias].fields[tabname][colname]; + if (tabname == 'dis_tab') { + var _o_tmp = []; + for each (var _o_col in rpt_rel_cache.order_by) { + if (_o_col.relation == relation_alias && _o_col.field == colname) continue; + _o_tmp.push( _o_col ); + } + rpt_rel_cache.order_by = _o_tmp + } + + with (rpt_rel_cache[relation_alias].fields) { + if ( getKeys(dis_tab).length == 0 && getKeys(filter_tab).length == 0 && getKeys(aggfilter_tab).length == 0 ) + delete rpt_rel_cache[relation_alias]; + } + } + + renderSources(); + + return true; +} + +function addReportAtoms () { + var nope = $( 'source-add' ).getAttribute('disabled'); + if (nope == 'true') return false; + + var class_tree = $('class-view'); + var transform_tree = $('trans-view'); + + var active_tab = filterByAttribute( + $('used-source-fields-tabbox').getElementsByTagName('tab'), + 'selected', + 'true' + )[0]; + + var tabname = active_tab.getAttribute('id') + + var items = getSelectedItems(class_tree); + var transform = getSelectedItems(transform_tree)[0]; + + var reltype = $('path-label').getAttribute('reltype'); + var class_label = $('path-label').value; + var relation_alias = hex_md5(class_label); + + for (var i in items) { + var item = items[i]; + + var class_path = item.getAttribute('fullpath'); + var field_class = item.getAttribute('idlclass'); + var datatype = item.getAttribute('datatype'); + var colname = item.getAttribute('idlfield'); + var field_label = item.firstChild.firstChild.getAttribute('label'); + + var table_name = getIDLClass(field_class).getAttributeNS(persistNS,'tablename'); + + if ( !rpt_rel_cache[relation_alias] ) { + rpt_rel_cache[relation_alias] = + { label : class_label, + alias : relation_alias, + path : class_path, + reltype : reltype, + idlclass : field_class, + table : table_name, + fields: { dis_tab : {}, filter_tab : {}, aggfilter_tab : {} } + }; + } + + if ( !rpt_rel_cache[relation_alias].fields[tabname][colname] ) { + rpt_rel_cache[relation_alias].fields[tabname][colname] = + { colname : colname, + transform : (transform && transform.getAttribute('name')) || 'Bare', + aggregate : transform && transform.getAttribute('aggregate'), + params : transform && transform.getAttribute('params'), + transform_label: (transform && transform.getAttribute('alias')) || 'Raw Data', + alias : field_label, + datatype : datatype, + op : '=', + op_label : 'Equals', + op_value : {}, + }; + + if (!rpt_rel_cache.order_by) + rpt_rel_cache.order_by = []; + + if (tabname == 'dis_tab') + rpt_rel_cache.order_by.push( { relation : relation_alias, field : colname } ); + + } else if ( + confirm( + 'You have already added the [' + field_label + + '] field\nfrom the [' + class_label + '] source. Click OK if you\nwould like ' + + 'to reset this field.' + ) + ) { + rpt_rel_cache[relation_alias].fields[tabname][colname] = + { colname : colname, + transform : (transform && transform.getAttribute('name')) || 'Bare', + aggregate : transform && transform.getAttribute('aggregate'), + params : transform && transform.getAttribute('params'), + transform_label: (transform && transform.getAttribute('alias')) || 'Raw Data', + alias : field_label, + datatype : datatype, + op : '=', + op_label : 'Equals', + op_value : {}, + }; + } + } + + renderSources(); + + return true; +} + +function changeDisplayOrder (dir) { + var active_tab = filterByAttribute( + $('used-source-fields-tabbox').getElementsByTagName('tab'), + 'selected', + 'true' + )[0]; + + var tabname = active_tab.getAttribute('id'); + + var tabpanel = $( tabname + 'panel' ); + var tree = tabpanel.getElementsByTagName('tree')[0]; + var item = getSelectedItems(tree)[0]; + + var item_pos = tree.view.selection.currentIndex; + + if (dir == 'u') { + if ( item.previousSibling ) { + item.parentNode.insertBefore( item, item.previousSibling ); + item_pos--; + } + } else if (dir == 'd') { + if ( item.nextSibling ) { + if (item.nextSibling.nextSibling ) item.parentNode.insertBefore( item, item.nextSibling.nextSibling ); + else item.parentNode.appendChild( item ); + item_pos++; + } + } + + rpt_rel_cache.order_by = []; + var ordered_list = tree.getElementsByTagName('treeitem'); + for (var i = 0; i < ordered_list.length; i++) { + rpt_rel_cache.order_by.push( + { relation : ordered_list[i].getAttribute('relation'), + field : ordered_list[i].firstChild.firstChild.nextSibling.getAttribute('label') + } + ); + } + + tree.view.selection.select( item_pos ); + return true; +} + +function alterColumnLabel () { + var active_tab = filterByAttribute( + $('used-source-fields-tabbox').getElementsByTagName('tab'), + 'selected', + 'true' + )[0]; + + var tabname = active_tab.getAttribute('id'); + + var tabpanel = $( tabname + 'panel' ); + var tree = tabpanel.getElementsByTagName('tree')[0]; + var item_pos = tree.view.selection.currentIndex; + + var item = getSelectedItems(tree)[0]; + var relation_alias = item.getAttribute('relation'); + + var field = item.firstChild.firstChild; + var colname = field.nextSibling.getAttribute('label'); + + var new_label = prompt( + "Change the column header to:", + field.getAttribute("label") + ); + + if (new_label) { + rpt_rel_cache[relation_alias].fields[tabname][colname].alias = new_label; + renderSources(true); + tree.view.selection.select( item_pos ); + tree.focus(); + tree.click(); + } + + return true; +} + +function alterColumnTransform (trans) { + + var transform = OILS_RPT_TRANSFORMS[trans]; + + var active_tab = filterByAttribute( + $('used-source-fields-tabbox').getElementsByTagName('tab'), + 'selected', + 'true' + )[0]; + + var tabname = active_tab.getAttribute('id'); + + var tabpanel = $( tabname + 'panel' ); + var tree = tabpanel.getElementsByTagName('tree')[0]; + var item_pos = tree.view.selection.currentIndex; + var item = getSelectedItems(tree)[0]; + var relation_alias = item.getAttribute('relation'); + + var field = item.firstChild.firstChild; + var colname = field.nextSibling.getAttribute('label'); + + rpt_rel_cache[relation_alias].fields[tabname][colname].transform = trans; + rpt_rel_cache[relation_alias].fields[tabname][colname].aggregate = transform.aggregate; + rpt_rel_cache[relation_alias].fields[tabname][colname].params = transform.params; + rpt_rel_cache[relation_alias].fields[tabname][colname].transform_label = transform.label; + + renderSources(true); + tree.view.selection.select( item_pos ); + tree.focus(); + tree.click(); + + $(tabname + '_trans_menu').hidePopup(); + return true; +} + +function changeOperator (args) { + + var active_tab = filterByAttribute( + $('used-source-fields-tabbox').getElementsByTagName('tab'), + 'selected', + 'true' + )[0]; + + var tabname = active_tab.getAttribute('id'); + + var tabpanel = $( tabname + 'panel' ); + var tree = tabpanel.getElementsByTagName('tree')[0]; + var item_pos = tree.view.selection.currentIndex; + var item = getSelectedItems(tree)[0]; + + var relation_alias = item.getAttribute('relation'); + + var field = item.firstChild.firstChild; + var colname = field.nextSibling.getAttribute('label'); + + rpt_rel_cache[relation_alias].fields[tabname][colname].op = args.op; + rpt_rel_cache[relation_alias].fields[tabname][colname].op_label = args.label; + + renderSources(true); + tree.view.selection.select( item_pos ); + tree.focus(); + tree.click(); + + $(tabname + '_op_menu').hidePopup(); + return true; +} + +function removeTemplateFilterValue () { + + var active_tab = filterByAttribute( + $('used-source-fields-tabbox').getElementsByTagName('tab'), + 'selected', + 'true' + )[0]; + + var tabname = active_tab.getAttribute('id'); + + var tabpanel = $( tabname + 'panel' ); + var tree = tabpanel.getElementsByTagName('tree')[0]; + var item_pos = tree.view.selection.currentIndex; + var items = getSelectedItems(tree); + + for (var i in items) { + var item = items[i]; + var relation_alias = item.getAttribute('relation'); + + var field = item.firstChild.firstChild; + var colname = field.nextSibling.getAttribute('label'); + + rpt_rel_cache[relation_alias].fields[tabname][colname].op_value = {}; + } + + renderSources(true); + tree.view.selection.select( item_pos ); + return true; +} + +function timestampSetDate (obj, cal, date) { + obj.op_value.value = date; + obj.op_value.object = cal.date; + obj.op_value.label = '"' + date + '"'; + + renderSources(true); + return true; +} + +var __handler_cache; + +function changeTemplateFilterValue () { + + var active_tab = filterByAttribute( + $('used-source-fields-tabbox').getElementsByTagName('tab'), + 'selected', + 'true' + )[0]; + + var tabname = active_tab.getAttribute('id'); + + var tabpanel = $( tabname + 'panel' ); + var tree = tabpanel.getElementsByTagName('tree')[0]; + var items = getSelectedItems(tree); + + var targetCmd = $( tabname + '_value_action' ); + + targetCmd.menu = null; + targetCmd.command = null; + targetCmd.oncommand = null; + targetCmd.removeEventListener( 'command', __handler_cache, true ); + + for (var i in items) { + var item = items[i]; + var relation_alias = item.getAttribute('relation'); + + var field = item.firstChild.firstChild; + var colname = field.nextSibling.getAttribute('label'); + + var obj = rpt_rel_cache[relation_alias].fields[tabname][colname] + var operation = OILS_RPT_FILTERS[obj.op]; + + switch (obj.datatype) { + case 'timestamp': + var cal_popup = $('calendar-widget'); + + while (cal_popup.firstChild) cal_popup.removeChild(cal_popup.lastChild); + var calendar = new Calendar( + 0, + obj.op_value.object, + function (cal,date) { return timestampSetDate(obj,cal,date) }, + function (cal) { cal_popup.hidePopup(); cal.destroy(); return true; } + ); + + var format = OILS_RPT_TRANSFORMS[obj.transform].cal_format || '%Y-%m-%d'; + + calendar.setDateFormat(format); + calendar.create(cal_popup); + + targetCmd.menu = 'calendar-widget'; + + break; + + case 'bool': + + function __bool_value_event_handler () { + var state, answer; + + try { + // get a reference to the prompt service component. + var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + + // set the buttons that will appear on the dialog. It should be + // a set of constants multiplied by button position constants. In this case, + // three buttons appear, Save, Cancel and a custom button. + var flags=promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 + + promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 + + promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_2; + + // display the dialog box. The flags set above are passed + // as the fourth argument. The next three arguments are custom labels used for + // the buttons, which are used if BUTTON_TITLE_IS_STRING is assigned to a + // particular button. The last two arguments are for an optional check box. + answer = promptService.select( + window, + "Boolean Value", + "Select the value, or cancel:", + 2, ["True", "False"], state + ); + } catch (e) { + answer = true; + state = confirm("Click OK for TRUE and Cancel for FALSE."); + state ? state = 0 : state = 1; + } + + if (answer) { + if (state) { + obj.op_value.value = 'f'; + obj.op_value.label = 'False'; + } else { + obj.op_value.value = 't'; + obj.op_value.label = 'True'; + } + } + + targetCmd.removeEventListener( 'command', __bool_value_event_handler, true ); + renderSources(true); + tree.view.selection.select( item_pos ); + return true; + } + + __handler_cache = __bool_value_event_handler; + targetCmd.addEventListener( 'command', __bool_value_event_handler, true ); + + break; + + default: + + + var promptstring = "Field does not match one of list (comma separated):"; + + switch (obj.op) { + case 'not between': + promptstring = "Field value is not between (comma separated):"; + break; + + case 'between': + promptstring = "Field value is between (comma separated):"; + break; + + case 'not in': + promptstring = "Field does not match one of list (comma separated):"; + break; + + case 'in': + promptstring = "Field matches one of list (comma separated):"; + break; + + default: + promptstring = "Value " + obj.op_label + ":"; + break; + } + + function __default_value_event_handler () { + var _v; + if (_v = prompt( promptstring, obj.op_value.value || '' )) { + switch (obj.op) { + case 'between': + case 'not between': + case 'not in': + case 'in': + obj.op_value.value = _v.split(/\s*,\s*/); + break; + + default: + obj.op_value.value = _v; + break; + } + + obj.op_value.label = '"' + obj.op_value.value + '"'; + } + + targetCmd.removeEventListener( 'command', __default_value_event_handler, true ); + renderSources(true); + tree.view.selection.select( item_pos ); + return true; + } + + __handler_cache = __default_value_event_handler; + targetCmd.addEventListener( 'command', __default_value_event_handler, true ); + + break; + } + } + + return true; +} + +function populateOperatorContext () { + + var active_tab = filterByAttribute( + $('used-source-fields-tabbox').getElementsByTagName('tab'), + 'selected', + 'true' + )[0]; + + var tabname = active_tab.getAttribute('id'); + + var tabpanel = $( tabname + 'panel' ); + var tree = tabpanel.getElementsByTagName('tree')[0]; + var items = getSelectedItems(tree); + + var dtypes = []; + for (var i in items) { + var item = items[i]; + dtypes.push( item.getAttribute('datatype') ); + } + + var menu = $(tabname + '_op_menu'); + while (menu.firstChild) menu.removeChild(menu.lastChild); + + for (var i in OILS_RPT_FILTERS) { + var o = OILS_RPT_FILTERS[i]; + menu.appendChild( + createMenuItem( + { label : o.label, + onmouseup : "changeOperator({op:'"+i+"',label:'"+o.label+"'})" + } + ) + ); + if (o.labels) { + var keys = getKeys(o.labels); + for ( var k in keys ) { + var key = keys[k]; + if ( grep(function(x){return key==x},dtypes).length ) { + menu.appendChild( + createMenuItem( + { label : o.labels[key], + onmouseup : "changeOperator({op:'"+i+"',label:'"+o.labels[key]+"'});" + } + ) + ); + } + } + } + } +} + +function populateTransformContext () { + + var active_tab = filterByAttribute( + $('used-source-fields-tabbox').getElementsByTagName('tab'), + 'selected', + 'true' + )[0]; + + var tabname = active_tab.getAttribute('id'); + + var tabpanel = $( tabname + 'panel' ); + var tree = tabpanel.getElementsByTagName('tree')[0]; + var items = getSelectedItems(tree); + + var transforms = {}; + for (var i in items) { + var item = items[i]; + var dtype = item.getAttribute('datatype'); + var item_transforms = getTransforms({ datatype : dtype }); + + for (var j in item_transforms) { + transforms[item_transforms[j]] = OILS_RPT_TRANSFORMS[item_transforms[j]]; + transforms[item_transforms[j]].name = item_transforms[j]; + } + } + + var transformList = []; + for (var i in transforms) { + transformList.push( transforms[i] ); + } + + transformList.sort( sortHashLabels ); + + var menu = $(tabname + '_trans_menu'); + while (menu.firstChild) menu.removeChild(menu.lastChild); + + for (var i in transformList) { + var t = transformList[i]; + + if (tabname.match(/filter/)) { + if (tabname.match(/^agg/)) { + if (!t.aggregate) continue; + } else { + if (t.aggregate) continue; + } + } + + menu.appendChild( + createMenuItem( + { aggregate : t.aggregate, + name : t.name, + alias : t.label, + label : t.label, + params : t.params, + onmouseup : "alterColumnTransform('"+t.name+"')" + } + ) + ); + } + + menu.parentNode.setAttribute('disabled','false'); + if (menu.childNodes.length == 0) { + menu.parentNode.setAttribute('disabled','true'); + } +} + + +function renderSources (selected) { + + var tree = $('used-sources'); + var sources = $('used-sources-treetop'); + var tabs = $('used-source-fields-tabbox').getElementsByTagName('tab'); + + if (!selected) { + while (sources.firstChild) sources.removeChild(sources.lastChild); + } else { + selected = getSelectedItems(tree); + if (!selected.length) { + selected = undefined; + while (sources.firstChild) sources.removeChild(sources.lastChild); + } + } + + for (var j = 0; j < tabs.length; j++) { + var tab = tabs[j]; + var tabname = tab.getAttribute('id') + var tabpanel = $( tab.getAttribute('id') + 'panel' ); + var fieldtree = tabpanel.getElementsByTagName('treechildren')[0]; + + while (fieldtree.firstChild) fieldtree.removeChild(fieldtree.lastChild); + } + + for (var relation_alias in rpt_rel_cache) { + if (!rpt_rel_cache[relation_alias].fields) continue; + + if (selected) { + if ( + !grep( + function (x) { + return x.getAttribute('relation') == relation_alias; + }, + selected + ).length + ) continue; + } else { + + $('used-sources-treetop').appendChild( + createTreeItem( + { relation : rpt_rel_cache[relation_alias].alias, + idlclass : rpt_rel_cache[relation_alias].idlclass, + reltype : rpt_rel_cache[relation_alias].reltype, + path : rpt_rel_cache[relation_alias].path, + }, + createTreeRow( + {}, + createTreeCell({ label : rpt_rel_cache[relation_alias].label }), + createTreeCell({ label : rpt_rel_cache[relation_alias].table }), + createTreeCell({ label : rpt_rel_cache[relation_alias].alias }), + createTreeCell({ label : rpt_rel_cache[relation_alias].reltype }) + ) + ) + ); + } + + for each (var tabname in ['filter_tab','aggfilter_tab']) { + tabpanel = $( tabname + 'panel' ); + fieldtree = tabpanel.getElementsByTagName('treechildren')[0]; + + for (var colname in rpt_rel_cache[relation_alias].fields[tabname]) { + with (rpt_rel_cache[relation_alias].fields[tabname][colname]) { + fieldtree.appendChild( + createTreeItem( + { relation : relation_alias, datatype : datatype }, + createTreeRow( + {}, + createTreeCell({ label : alias }), + createTreeCell({ label : colname }), + createTreeCell({ label : datatype }), + createTreeCell({ label : transform_label }), + createTreeCell({ label : transform }) + ) + ) + ); + + fieldtree.lastChild.firstChild.appendChild( + createTreeCell({ op : op, label : op_label }) + ); + + if (op_value.value != undefined) { + fieldtree.lastChild.firstChild.appendChild( + createTreeCell({ label : op_value.label }) + ); + } + } + } + } + } + + tabpanel = $( 'dis_tabpanel' ); + fieldtree = tabpanel.getElementsByTagName('treechildren')[0]; + for each (var order in rpt_rel_cache.order_by) { + + if (selected) { + if ( + !grep( + function (x) { + return x.getAttribute('relation') == order.relation; + }, + selected + ).length + ) continue; + } + + with (rpt_rel_cache[order.relation].fields.dis_tab[order.field]) { + fieldtree.appendChild( + createTreeItem( + { relation : order.relation, datatype : datatype }, + createTreeRow( + {}, + createTreeCell({ label : alias }), + createTreeCell({ label : colname }), + createTreeCell({ label : datatype }), + createTreeCell({ label : transform_label }), + createTreeCell({ label : transform }) + ) + ) + ); + + fieldtree.lastChild.firstChild.appendChild( + createTreeCell({ op : op, label : op_label }) + ); + + if (op_value.value != undefined) { + fieldtree.lastChild.firstChild.appendChild( + createTreeCell({ label : op_value.label }) + ); + } + } + } +} + +var param_count; +var tab_use = { + dis_tab : 'select', + filter_tab : 'where', + aggfilter_tab : 'having', +}; + + +function save_template () { + param_count = 0; + + var template = { + version : 2, + core_class : $('sources-treetop').getElementsByTagName('treeitem')[0].getAttribute('idlclass'), + select : [], + from : {}, + where : [], + having : [], + order_by : [] + }; + + for (var relname in rpt_rel_cache) { + var relation = rpt_rel_cache[relname]; + if (!relation.fields) continue; + + // first, add the where and having clauses (easier) + for each (var tab_name in [ 'filter_tab', 'aggfilter_tab' ]) { + var tab = relation.fields[tab_name]; + for (var field in tab) { + fleshTemplateField( template, relation, tab_name, field ); + } + } + + // now the from clause + fleshFromPath( template, relation ); + } + + // and now for select (based on order_by) + for each (var order in rpt_rel_cache.order_by) + fleshTemplateField( template, rpt_rel_cache[order.relation], 'dis_tab', order.field ); + + template.rel_cache = rpt_rel_cache; + + //prompt( 'template', js2JSON( template ) ); + + // and the saving throw ... + var cgi = new CGI(); + var session = cgi.param('ses'); + fetchUser( session ); + + var tmpl = new rt(); + tmpl.name( $('template-name').value ); + tmpl.description( $('template-description').value ); + tmpl.owner(USER.id()); + tmpl.folder(cgi.param('folder')); + tmpl.data(js2JSON(template)); + + if(!confirm('Name : '+tmpl.name() + '\nDescription: ' + tmpl.description()+'\nSave Template?')) + return; + + var req = new Request('open-ils.reporter:open-ils.reporter.template.create', session, tmpl); + req.request.alertEvent = false; + req.callback( + function(r) { + var res = r.getResultObject(); + if(checkILSEvent(res)) { + alertILSEvent(res); + } else { + if( res && res != '0' ) { + confirm('Template ' + tmpl.name() + ' was successfully saved.'); + _l('../oils_rpt.xhtml'); + } + } + } + ); + + req.send(); +} + +function fleshFromPath ( template, rel ) { + var table_path = rel.path.split( /\./ ); + if (table_path.length > 1 || rel.path.indexOf('-') > -1) table_path.push( rel.idlclass ); + + var prev_type = ''; + var prev_link = ''; + var current_path = ''; + var current_obj = template.from; + var link; + while (link = table_path.shift()) { + if (current_path) current_path += '-'; + current_path += link; + + var leaf = table_path.length == 0 ? true : false; + + current_obj.path = current_path; + current_obj.table = getIDLClass( link.split(/-/)[0] ).getAttributeNS( persistNS, 'tablename' ); + + if (prev_link != '') { + var prev_class = getIDLClass( prev_link.split(/-/)[0] ); + var prev_field = prev_link.split(/-/)[1]; + + var current_link = getIDLLink( prev_class, prev_field ); + current_obj.key = current_link.getAttribute('key'); + + if ( + current_link.getAttribute('reltype') != 'has_a' || + prev_type == 'left' || + rel.reltype != 'has_a' + ) current_obj.type = 'left'; + + prev_type = current_obj.type; + } + + if (leaf) { + current_obj.label = rel.label; + current_obj.alias = rel.alias; + current_obj.idlclass = rel.idlclass; + current_obj.template_path = rel.path; + } else { + var current_class = getIDLClass( link.split(/-/)[0] ); + var join_field = link.split(/-/)[1]; + var join_link = getIDLLink(current_class, join_field); + + if (join_link.getAttribute('reltype') != 'has_a') { + var fields_el = current_class.getElementsByTagName('fields')[0]; + join_field = fields_el.getAttributeNS(persistNS, 'primary') + '-' + join_link.getAttribute('key'); + } + + current_obj.alias = hex_md5( current_path ) ; + join_field += '-' + current_obj.alias; + if (table_path.length == 1) join_field += '-' + rel.alias; + + if (!current_obj.join) current_obj.join = {}; + if (!current_obj.join[join_field]) current_obj.join[join_field] = {}; + + if (current_obj.type == 'left') current_obj.join[join_field].type = 'left'; + + current_obj = current_obj.join[join_field]; + } + + prev_link = link; + } + + +} + +function fleshTemplateField ( template, rel, tab_name, field ) { + + if (!rel.fields[tab_name] || !rel.fields[tab_name][field]) return; + + var tab = rel.fields[tab_name]; + + var table_path = rel.path.split( /\./ ); + if (table_path.length > 1 || rel.path.indexOf('-') > -1) + table_path.push( rel.idlclass ); + + table_path.push( field ); + + var field_path = table_path.join('-'); + + var element = { + alias : tab[field].alias, + column : + { colname : field, + transform : tab[field].transform, + transform_label : tab[field].transform_label + }, + path : field_path, + relation : rel.alias + }; + + if (tab_name.match(/filter/)) { + element.condition = {}; + element.condition[tab[field].op] = + tab[field].op_value.value ? + tab[field].op_value.value : + '::P' + param_count++; + } + + template[tab_use[tab_name]].push(element); +} + diff --git a/Open-ILS/web/reports/xul/template_builder.xul b/Open-ILS/web/reports/xul/template_builder.xul new file mode 100644 index 0000000000..434066e912 --- /dev/null +++ b/Open-ILS/web/reports/xul/template_builder.xul @@ -0,0 +1,315 @@ + + + + + + + +