LP#1332138 Report template / field documentation
authorMike Rylander <mrylander@gmail.com>
Mon, 12 May 2014 18:13:25 +0000 (14:13 -0400)
committerBen Shum <bshum@biblio.org>
Fri, 8 Aug 2014 01:51:56 +0000 (21:51 -0400)
* Report templates support a new External URL field, which may
  contain a link to template-specific, local documentation.

 ** When set, a link to the external documentation will be
    displayed in a new column in the template list and within
    the report editor.

 ** URL's are set in the 'Documentation URL' entry.

* Template display fields and filters support a new Field Hint
  value.  When set, hints are displayed in the report editor.

 ** Values are set via the 'Change Field Hint' option along
    the bottom of the template editor.

------ from commits ----

* Initial template-level support for inline template and field documentation
* Use external documenation url in clark output
* Show the docs link and strings in the report editor
* Set the href of a link, instead of the text content of a span
* Attempt to pop up a new window (may require more pushups in the XUL staff client)
* Add calculated column support, use it for external docs url
* Realign field names

Signed-off-by: Mike Rylander <mrylander@gmail.com>
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Signed-off-by: Kathy Lussier <klussier@masslnc.org>
Signed-off-by: Ben Shum <bshum@biblio.org>
13 files changed:
Open-ILS/src/reporter/clark-kent.pl
Open-ILS/src/reporter/report-fail
Open-ILS/src/reporter/report-success
Open-ILS/web/js/dojo/openils/reports/nls/reports.js
Open-ILS/web/opac/common/js/fm_table.js
Open-ILS/web/opac/common/js/fm_table_conf.js
Open-ILS/web/opac/locale/en-US/reports.dtd
Open-ILS/web/reports/oils_rpt_editor.xhtml
Open-ILS/web/reports/oils_rpt_param_editor.js
Open-ILS/web/reports/oils_rpt_report_editor.js
Open-ILS/web/reports/oils_rpt_utils.js
Open-ILS/web/reports/xul/template-config.js
Open-ILS/web/reports/xul/template_builder.xul

index 9030825..2e9bbd9 100755 (executable)
@@ -373,6 +373,11 @@ sub send_success {
        $tmpl =~ s/{COMPLETE_TIME}/$r->{complete_time}/smog;
        $tmpl =~ s/{OUTPUT_URL}/$url/smog;
 
+       my $tdata = OpenSRF::Utils::JSON->JSON2perl( $r->{report}->{template}->{data} );
+       if ($$tdata{version} >= 4) {
+               $tmpl =~ s/{EXTERNAL_URL}/$$tdata{doc_url}/smog;
+       }
+
        my $sender = Email::Send->new({mailer => 'SMTP'});
        $sender->mailer_args([Host => $email_server]);
        $sender->send($tmpl);
@@ -394,6 +399,11 @@ sub send_fail {
        $tmpl =~ s/{ERROR_TEXT}/$r->{error_text}/smog;
        $tmpl =~ s/{SQL}/$sql/smog;
 
+       my $tdata = OpenSRF::Utils::JSON->JSON2perl( $r->{report}->{template}->{data} );
+       if ($$tdata{version} >= 4) {
+               $tmpl =~ s/{EXTERNAL_URL}/$$tdata{doc_url}/smog;
+       }
+
        my $sender = Email::Send->new({mailer => 'SMTP'});
        $sender->mailer_args([Host => $email_server]);
        $sender->send($tmpl);
@@ -438,6 +448,8 @@ sub build_html {
        my $r = shift;
 
        my $index = new FileHandle (">$file") or die "Cannot write to '$file'";
+
+       my $tdata = OpenSRF::Utils::JSON->JSON2perl( $r->{report}->{template}->{data} );
        
        # index header
        print $index <<"        HEADER";
@@ -456,9 +468,15 @@ sub build_html {
        <body>
                <center>
                <h2><u>$$r{report}{name}</u></h2>
-               $$r{report}{description}<br/><br/><br/>
+               $$r{report}{description}<br/>
        HEADER
 
+       if ($$tdata{version} >= 4 and $$tdata{doc_url}) {
+               print $index "<a target='_blank' href='$$tdata{doc_url}'>External template documentation</a><br/>";
+       }
+
+       print $index "<br/><br/>";
+
        my @links;
 
     my $br4 = '<br/>' x 4;
@@ -478,11 +496,15 @@ sub build_html {
        print $debug "<html><head><meta charset='utf-8'><title>DEBUG: $$r{report}{name}</title></head><body>";
 
        {       no warnings;
+               if ($$tdata{version} >= 4 and $$tdata{doc_url}) {
+                       print $debug "<b><a target='_blank' href='$$tdata{doc_url}'>External template documentation</a></b><br/><a href='report-data.html'>Back to output index</a><hr/>";
+               }
+
                print $debug '<h1>Generated SQL</h1><pre>' . $r->{resultset}->toSQL() . "</pre><a href='report-data.html'>Back to output index</a><hr/>";
                print $debug '<h1>Template</h1><pre>' . Dumper( $r->{report}->{template} ) . "</pre><a href='report-data.html'>Back to output index</a><hr/>";
-               print $debug '<h1>Template Data</h1><pre>' . Dumper( OpenSRF::Utils::JSON->JSON2perl( $r->{report}->{template}->{data} ) ) . "</pre><a href='report-data.html'>Back to output index</a><hr/>";
+               print $debug '<h1>Template Data</h1><pre>' . Dumper( $tdata ) . "</pre><a href='report-data.html'>Back to output index</a><hr/>";
                print $debug '<h1>Report Parameter</h1><pre>' . Dumper( $r->{report} ) . "</pre><a href='report-data.html'>Back to output index</a><hr/>";
-               print $debug '<h1>Report Parameter Data</h1><pre>' . Dumper( OpenSRF::Utils::JSON->JSON2perl( $r->{report}->{data} ) ) . "</pre><a href='report-data.html'>Back to output index</a><hr/>";
+               print $debug '<h1>Report Parameter Data</h1><pre>' . Dumper( $tdata ) . "</pre><a href='report-data.html'>Back to output index</a><hr/>";
                print $debug '<h1>Report Run Time</h1><pre>' . $r->{resultset}->relative_time . "</pre><a href='report-data.html'>Back to output index</a><hr/>";
                print $debug '<h1>OpenILS::Reporter::SQLBuilder::ResultSet Object</h1><pre>' . Dumper( $r->{resultset} ) . "</pre><a href='report-data.html'>Back to output index</a>";
        }
index d49a13a..569bc54 100644 (file)
@@ -8,6 +8,9 @@ has failed with the following error message:
 
  {ERROR_TEXT}
 
+External documentation for the template is available at the following URL:
+ {EXTERNAL_URL}
+
 The SQL command attempted was:
 
  {SQL}
index a49a1a8..873943d 100644 (file)
@@ -8,3 +8,6 @@ time of {COMPLETE_TIME}.
 
 You can view the report at the following URL:
 {OUTPUT_URL}
+
+External documentation for the template is available at the following URL:
+{EXTERNAL_URL}
index 34bd06f..1ae57f6 100644 (file)
        "TEMPLATE_CONF_EQUALS": "Equals",
        "TEMPLATE_CONF_CONFIRM_RESET": "You have already added the [${0}] field\nfrom the [${1}] source. Click OK if you\nwould like to reset this field.",
        "TEMPLATE_CONF_PROMPT_CHANGE": "Change the column header to: ${0}",
+       "TEMPLATE_FIELD_DOC_PROMPT_CHANGE": "Change the field hint to:",
        "TEMPLATE_CONF_BOOLEAN_VALUE": "Boolean Value",
        "TEMPLATE_CONF_SELECT_CANCEL": "Select the value, or cancel:",
        "TEMPLATE_CONF_TRUE": "True",
index a0608eb..11602db 100644 (file)
@@ -151,6 +151,7 @@ FMObjectBuilder.prototype.setKeys = function(o) {
                this.bold = this.display[o.classname].bold;
                this.money = this.display[o.classname].money;
                this.sortdata = this.display[o.classname].sortdata;
+               this.calculate = this.display[o.classname].calculate;
        }
 
        if(!this.keys && FM_TABLE_DISPLAY[o.classname])
@@ -165,6 +166,9 @@ FMObjectBuilder.prototype.setKeys = function(o) {
        if(!this.sortdata && FM_TABLE_DISPLAY[o.classname])
                this.sortdata = FM_TABLE_DISPLAY[o.classname].sortdata;
 
+       if(!this.calculate && FM_TABLE_DISPLAY[o.classname])
+               this.calculate = FM_TABLE_DISPLAY[o.classname].calculate;
+
        if(!this.keys) {
                this.keys = fmclasses[o.classname];
                sortme = true;
@@ -193,9 +197,15 @@ FMObjectBuilder.prototype.buildObjectRow = function(obj) {
 
        for( var i = 0; i < this.keys.length; i++ ) {
                var td = elem('td');    
-               var data = obj[this.keys[i]]();
-               data = this.munge(data);
-               this.fleshData(td, data, this.keys[i]);
+        var data = '';
+        if (this.caclulate[i]) { // fake data! pass the object
+            td.appendChild(this.calculate[i](obj);
+        } else {
+                   data = obj[this.keys[i]]();
+               data = this.munge(data);
+               this.fleshData(td, data, this.keys[i]);
+        }
+
                row.appendChild(td);
        }
        this.tbody.appendChild(row);
index e5db887..b7f2887 100644 (file)
@@ -106,9 +106,23 @@ var FM_TABLE_DISPLAY = {
                fields : [
                        'name',
                        'description',
+            'docs',
                        'create_time',
                        'owner',
                ],
+        calculate : {
+            docs : function (t) {
+                var d = JSON2js(t.data());
+                if (d.version >= 4 && d.doc_url) {
+                    return elem(
+                        'a',
+                        { href : d.doc_url, target : '_blank'},
+                        'External Documentation'
+                    );
+                }
+                return text('');
+            }
+        },
                sortdata : [ 'name', 1 ]
        },
        'rs' : {
index f572515..2c9e8f9 100644 (file)
@@ -69,6 +69,7 @@
 
 <!ENTITY reports.oils_rpt_editor.template_name "Template Name:">
 <!ENTITY reports.oils_rpt_editor.template_creator "Template Creator:">
+<!ENTITY reports.oils_rpt_editor.doc_url "Template Documentation URL:">
 <!ENTITY reports.oils_rpt_editor.template_description "Template Description:">
 <!ENTITY reports.oils_rpt_editor.report_name "Report Name:">
 <!ENTITY reports.oils_rpt_editor.report_description "Report Description:">
 <!ENTITY reports.xul.template_builder.template_config_caption.label "Template Configuration">
 <!ENTITY reports.xul.template_builder.name.label "Name:">
 <!ENTITY reports.xul.template_builder.description.label "Description:">
+<!ENTITY reports.xul.template_builder.doc_url.label "Documentation URL:">
 <!ENTITY reports.xul.template_builder.save.label "Save">
 <!ENTITY reports.xul.template_builder.displayed_fields.label "Displayed Fields">
 <!ENTITY reports.xul.template_builder.base_filters.label "Base Filters">
 <!ENTITY reports.xul.template_builder.change_transform.label "Change Transform">
 <!ENTITY reports.xul.template_builder.change_operator.label "Change Operator">
 <!ENTITY reports.xul.template_builder.change_value.label "Change value">
+<!ENTITY reports.xul.template_builder.change_field_doc.label "Change Field Hint">
 <!ENTITY reports.xul.template_builder.remove_value.label "Remove value">
 <!ENTITY reports.xul.template_builder.remove_selected_fields.label "Remove Selected Fields">
 <!ENTITY reports.xul.template_builder.table_name.label "Table Name">
index 0c5dcc7..c2cf708 100644 (file)
@@ -13,6 +13,9 @@
                                <th>&reports.oils_rpt_editor.template_description;</th>
                                <td><span id='oils_rpt_report_editor_template_description'></span></td>
                        </tr>
+                       <tr id='oils_rpt_report_editor_template_doc_url_row'>
+                               <th colspan=2><a target="_blank" href="" id='oils_rpt_report_editor_template_doc_url'>&reports.oils_rpt_editor.doc_url;</a></th>
+                       </tr>
 
                        <tr>
                                <th>&reports.oils_rpt_editor.report_name;</th>
index d99c7b0..947f064 100644 (file)
@@ -37,7 +37,7 @@ oilsRptParamEditor.prototype.draw = function() {
                var par = params[p];
                var row = oilsRptParamEditor.row.cloneNode(true);
                this.tbody.appendChild(row);
-               $n(row, 'column').appendChild(text(oilsRptMakeLabel(par.path)));
+               $n(row, 'column').appendChild(text(oilsRptMakeLabel(par.path, par.field_doc)));
                $n(row, 'transform').appendChild(text(OILS_RPT_TRANSFORMS[par.column.transform].label));
                $n(row, 'action').appendChild(text(OILS_RPT_FILTERS[par.op].label));
                par.widget = this.buildWidget(par, $n(row, 'widget'));
@@ -52,7 +52,7 @@ oilsRptParamEditor.prototype.draw = function() {
                var par = tparams[p];
                var row = oilsRptParamEditor.row.cloneNode(true);
                this.tbody.appendChild(row);
-               $n(row, 'column').appendChild(text(oilsRptMakeLabel(par.path)));
+               $n(row, 'column').appendChild(text(oilsRptMakeLabel(par.path, par.field_doc)));
                $n(row, 'transform').appendChild(text(OILS_RPT_TRANSFORMS[par.column.transform].label));
                $n(row, 'action').appendChild(text(OILS_RPT_FILTERS[par.op].label));
                par.widget = this.buildWidget(par, $n(row, 'widget'));
index ed9d564..41c8db4 100644 (file)
@@ -16,6 +16,12 @@ function oilsRptReportEditor(rptObject, folderWindow) {
        appendClear(DOM.oils_rpt_report_editor_template_name, tmpl.name());
        appendClear(DOM.oils_rpt_report_editor_template_creator, tmpl.owner().usrname());
        appendClear(DOM.oils_rpt_report_editor_template_description, tmpl.description());
+
+    if (rptObject.def.version >= 4) {
+           DOM.oils_rpt_report_editor_template_doc_url.setAttribute( 'href', rptObject.def.doc_url );
+    } else {
+        hideMe(DOM.oils_rpt_report_editor_template_doc_url_row);
+    }
     
     appendClear(DOM.oils_rpt_report_editor_cols,'');
        iterate(rptObject.def.select, 
index 676d4c7..3d7602d 100644 (file)
@@ -89,7 +89,7 @@ function oilsRptPathRel(path) {
 }
 
 /* creates a label "path" based on the column path */
-function oilsRptMakeLabel(path) {
+function oilsRptMakeLabel(path, hint) {
        var parts = path.split(/-/);
        var str = '';
        for( var i = 0; i < parts.length; i++ ) {
@@ -103,6 +103,11 @@ function oilsRptMakeLabel(path) {
                        str += " -> "+f.label;
                }
        }
+
+    if (hint) {
+        str += '<br/><b><i>' + hint + '</i></b>'
+    }
+
        return str;
 }
 
index e936880..69f70d2 100644 (file)
@@ -80,6 +80,7 @@ function addReportAtoms () {
                var colname = item.getAttribute('idlfield');
                var jointype = item.getAttribute('join');
                var field_label = item.firstChild.firstChild.getAttribute('label');
+               var field_doc = item.firstChild.lastChild.getAttribute('label');
 
                var table_name = getSourceDefinition(field_class);
 
@@ -104,6 +105,7 @@ function addReportAtoms () {
                                  params    : transform && transform.getAttribute('params'),
                                  transform_label: (transform && transform.getAttribute('alias')) || rpt_strings.TEMPLATE_CONF_RAW_DATA,
                                  alias     : field_label,
+                                 field_doc : field_doc,
                                  join      : jointype,
                                  datatype  : datatype,
                                  op        : (datatype == 'array') ? '= any' : '=',
@@ -125,6 +127,7 @@ function addReportAtoms () {
                                  params    : transform && transform.getAttribute('params'),
                                  transform_label: (transform && transform.getAttribute('alias')) || rpt_strings.TEMPLATE_CONF_RAW_DATA,
                                  alias     : field_label,
+                                 field_doc : field_doc,
                                  join      : jointype,
                                  datatype  : datatype,
                                  op        : '=',
@@ -214,6 +217,39 @@ function alterColumnLabel () {
        return true;
 }
 
+function changeFieldDoc () {
+       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.lastChild;
+       var colname = item.firstChild.firstChild.nextSibling.getAttribute('label');
+
+       var old_label = field.getAttribute("label");
+       var new_label = prompt( rpt_strings.TEMPLATE_FIELD_DOC_PROMPT_CHANGE, old_label );
+
+       if (new_label) {
+               rpt_rel_cache[relation_alias].fields[tabname][colname].field_doc = 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];
@@ -695,7 +731,15 @@ function renderSources (selected) {
                                                fieldtree.lastChild.firstChild.appendChild(
                                                        createTreeCell({ label : op_value.label })
                                                );
-                                       }
+                                       } else {
+                                               fieldtree.lastChild.firstChild.appendChild(
+                                                       createTreeCell({})
+                                               );
+                    }
+
+                                       fieldtree.lastChild.firstChild.appendChild(
+                                               createTreeCell({ label : field_doc })
+                                       );
                                }
                        }
                }
@@ -726,20 +770,11 @@ function renderSources (selected) {
                                                createTreeCell({ label : colname }),
                                                createTreeCell({ label : datatype }),
                                                createTreeCell({ label : transform_label }),
-                                               createTreeCell({ label : transform })
+                                               createTreeCell({ label : transform }),
+                                               createTreeCell({ label : field_doc })
                                        )
                                )
                        );
-
-                       fieldtree.lastChild.firstChild.appendChild(
-                               createTreeCell({ op : op, label : op_label })
-                       );
-
-                       if (op_value.value != undefined) {
-                               fieldtree.lastChild.firstChild.appendChild(
-                                       createTreeCell({ label : op_value.label })
-                               );
-                       }
                }
        }
 }
@@ -756,7 +791,8 @@ function save_template () {
        param_count = 0;
 
        var template = {
-               version    : 3,
+               version    : 4,
+               doc_url   : $('template-doc-url').value,
                core_class : $('sources-treetop').getElementsByTagName('treeitem')[0].getAttribute('idlclass'),
                select     : [],
                from       : {},
@@ -942,6 +978,7 @@ function fleshTemplateField ( template, rel, tab_name, field ) {
 
        var element = {
                alias : tab[field].alias,
+               field_doc : tab[field].field_doc,
                column :
                        { colname : field,
                          transform : tab[field].transform,
index 2a03b23..5b27102 100644 (file)
                        <vbox>
                                <label control="template-name" value="&reports.xul.template_builder.name.label;" style="height:2em"/>
                                <label control="template-description" value="&reports.xul.template_builder.description.label;"/>
+                               <label control="template-doc-url" value="&reports.xul.template_builder.doc_url.label;"/>
                        </vbox>
                        <vbox flex="1">
                                <textbox id="template-name" flex="1"/>
                                <textbox id="template-description" multiline="true" flex="1" style="max-height:3em"/>
+                               <textbox id="template-doc-url" flex="1"/>
                        </vbox>
                        <vbox pack="end">
                                <button onclick="save_template();" label="&reports.xul.template_builder.save.label;"/>
                                                                                        <treecol label="&reports.xul.template_builder.data_type.label;" flex="1"/>
                                                                                        <treecol label="&reports.xul.template_builder.field_transform.label;" flex="1"/>
                                                                                        <treecol label="&reports.xul.template_builder.field_transform_type.label;" hidden="true" flex="1"/>
+                                                                                       <treecol label="&reports.xul.template_builder.field_doc.label;" hidden="true" flex="1"/>
                                                                                </treecols>
                                                                                <treechildren id="dis-col-treetop" alternatingbackground="true" />
                                                                        </tree>
                                                                        <button label="&reports.xul.template_builder.move_down.label;" oncommand="changeDisplayOrder('d')"/>
                                                                        <spacer flex="1"/>
                                                                        <button label="&reports.xul.template_builder.remove_selected_field.label;" oncommand="removeReportAtom()"/>
+                                                                       <spacer flex="1"/>
+                                                                       <button label="&reports.xul.template_builder.change_field_doc.label;" oncommand="changeFieldDoc()"/>
                                                                </hbox>
                                                        </vbox>
                                                </tabpanel>
                                                                                        <treecol label="&reports.xul.template_builder.field_transform_type.label;" hidden="true" flex="1"/>
                                                                                        <treecol label="&reports.xul.template_builder.operator.label;" flex="1"/>
                                                                                        <treecol label="&reports.xul.template_builder.value.label;" flex="1"/>
+                                                                                       <treecol label="&reports.xul.template_builder.field_doc.label;" hidden="true" flex="1"/>
                                                                                </treecols>
                                                                                <treechildren id="filter-col-treetop" alternatingbackground="true" />
                                                                        </tree>
                                                                        <button label="&reports.xul.template_builder.remove_value.label;" oncommand="removeTemplateFilterValue()"/>
                                                                        <spacer flex="1"/>
                                                                        <button label="&reports.xul.template_builder.remove_selected_fields.label;" oncommand="removeReportAtom()"/>
+                                                                       <spacer flex="1"/>
+                                                                       <button label="&reports.xul.template_builder.change_field_doc.label;" oncommand="changeFieldDoc()"/>
                                                                </hbox>
                                                        </vbox>
                                                </tabpanel>
                                                                                        <treecol label="&reports.xul.template_builder.field_transform_type.label;" hidden="true" flex="1"/>
                                                                                        <treecol label="&reports.xul.template_builder.operator.label;" flex="1"/>
                                                                                        <treecol label="&reports.xul.template_builder.value.label;" flex="1"/>
+                                                                                       <treecol label="&reports.xul.template_builder.field_doc.label;" hidden="true" flex="1"/>
                                                                                </treecols>
                                                                                <treechildren id="aggfilter-col-treetop" alternatingbackground="true" />
                                                                        </tree>
                                                                        <button label="&reports.xul.template_builder.remove_value.label;" oncommand="removeTemplateFilterValue()"/>
                                                                        <spacer flex="1"/>
                                                                        <button label="&reports.xul.template_builder.remove_selected_fields.label;" oncommand="removeReportAtom()"/>
+                                                                       <spacer flex="1"/>
+                                                                       <button label="&reports.xul.template_builder.change_field_doc.label;" oncommand="changeFieldDoc()"/>
                                                                </hbox>
                                                        </vbox>
                                                </tabpanel>