Export Decoration

Abstract

Learn how to add custom code to the HTML head and body elements within Pull Reports™ Ad Hoc reports.


Table of Contents

Two Export Report REST API formats, html and map, return complete HTML documents to be displayed in a web browser. By default, the HTML is undecorated such that the Pull Reports™ export content fills the entire <body> element.

Pull Reports™ provides two techniques to decorate the exported HTML with additional HTML content: Servlet includes and SiteMesh.

Servlet Includes

Configure the inclusion of html or jsp content via export.report.include-prefixed configuration properties. The value of these properties is a resource path within the deployed WAR to be include via RequestDispatcher#include into one of three places within the HTML DOM

  • before the close of the <head> element

  • after the start of the <body> element

  • before the close of the <body> element

The export.report.include-prefixed configuration properties may be specified multiple times within pullreports.properties to specify or override behavior for specific formats, HTTP response codes, <catalog>s, or <report>s.

export.report.include property name structure

The structure of export.report.include-prefixed configuration property names is:

export.report.include[.html|.map].(head.end|body.start|body.end)[.400|.500][.catalogId][.reportId]

Where (...) demarcated terms are required and [...] demarcated terms are optional. The | character separates the permitted values for a particular term.

When resolving an inclusion value for a particular report, Pull Reports™ follows these rules to determine precedence:

  • Properties specified for a particular report via [.catalogId][.reportId] take precedence over values specified for a particular catalog via [.catalogId] which take precedence over values specified without reference to a catalog or report.

  • Properties specified for either the html or map format via [.html|.map] take precedence over values specified without a format.

  • In the case of a 400 or 500 HTTP response, properties specified with a [.400|.500] status code take precedence over values specified without status.

Simple example:  Here is a simple example which includes a navigation.html header at the top of all html and map export formats:

export.report.include.body.start=/WEB-INF/content/navigation.html

To override the property for a all <report>s in <catalog id='my-catalog'>, add a second property suffixed with the catalog id:

export.report.include.body.start=/WEB-INF/content/navigation.html
export.report.include.body.start.my-catalog=/WEB-INF/content/my-catalog-navigation.html

See additional configuration examples below.

export.report.include property name terms

[.html|.map]

Specify either the html or map term to apply the property to the html or map format respectively. If not specified, the value of the property applies to both formats.

(head.end|body.start|body.end) (Required)

Specify the inclusion location with the HTML document via one of the three following terms.

  • head.end: before the close of the <head> element

  • body.start: after the start of the <body> element

  • body.end: before the close of the <body> element

[.400|.500]

Specify either the 400 or 500 term to apply the property to the respective HTML response code. If not specified, the value of the property applies to all response codes.

[.catalogId][.reportId]

Specify a <catalog> id and, optionally, <report> id to limit the property to a specific catalog or report.

export.report.include property examples

The following example pullreports.properties contains several permutations of the export.report.include-prefixed properties with explanatory comments.

# Configures default body.start and body.end includes for all responses 
# of 500 (error) status
export.report.include.body.start.500=/WEB-INF/error-header.html
export.report.include.body.end.500=/WEB-INF/error-footer.html

# Configures default body.start and body.end includes for all responses 
# of 400 (bad request / validation error) status
export.report.include.body.start.400=/WEB-INF/validation-error-header.html
export.report.include.body.end.400=/WEB-INF/validation-error-footer.html

# Configures default head.end include for all export results of format type 
# map. The included file might contain CSS or JavaScript code to customize 
# the map export.
export.report.include.map.head.end=/WEB-INF/map-head-include.jsp

# Configures default body.start and body.end includes for all responses. 
# These includes could contain common header navigation and common footer 
# elements respectively.
export.report.include.body.start=/WEB-INF/page-header.jsp
export.report.include.body.end=/WEB-INF/page-footer.jsp

# Override the includes for <catalog id='my-catalog'>.
export.report.include.body.start.my-catalog=/WEB-INF/my-catalog-page-header.jsp
export.report.include.body.end.my-catalog=/WEB-INF/my-catalog-page-footer.jsp

# Override the 400 head include for the html format for <report id='my-report'> 
# within <catalog id='my-catalog'>.
export.report.include.html.head.end.400.my-catalog.my-report=\
/WEB-INF/validation-error-html-head-include-my-report.html

SiteMesh

Follow these steps to decorate the exported HTML with the SiteMesh framework. The following steps assume SiteMesh version 2.4.

  1. Make the SiteMesh JAR available to the web application into which Pull Reports™ is installed by placing the JAR within the WEB-INF/lib directory.

    If using Gradle for dependency management, add the following dependency to include SiteMesh:

    runtime 'opensymphony:sitemesh:2.4.2'

  2. Install the SiteMesh filter in web.xml.

    <filter>
      <filter-name>sitemesh</filter-name>
      <filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
    </filter>
    <filter-mapping>
      <filter-name>sitemesh</filter-name>
      <url-pattern>/pullreports/catalog/*</url-pattern>
    </filter-mapping>

  3. Install the sitemesh.xml file within the WEB-INF directory. Configure a ParameterDecoratorMapper within the <decorator-mappers> list. The value attribute must be set to format to correspond to the Export Report REST API format parameter.

    <?xml version="1.0" encoding="UTF-8"?>
    <sitemesh>
        <property name="decorators-file" value="/WEB-INF/decorators.xml" />
        <excludes file="${decorators-file}"/>
        <page-parsers>
            <parser content-type="text/html" class="com.opensymphony.module.sitemesh.parser.HTMLPageParser" />
        </page-parsers>
        <decorator-mappers>
            <mapper class="com.opensymphony.module.sitemesh.mapper.ParameterDecoratorMapper">
                <param name="decorator.parameter" value="format" />
            </mapper>
            <mapper class="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper">
                <param name="config" value="${decorators-file}" />
            </mapper>
        </decorator-mappers>
    </sitemesh>
  4. Install the decorators.xml file within the WEB-INF directory. Configure two <decorators> with the names html and map respectively.

    <?xml version="1.0" encoding="UTF-8"?>
    <decorators defaultdir="/WEB-INF/decorators">
        <decorator name="html" page="my-html-decorator.jsp"></decorator>
        <decorator name="map" page="my-map-decorator.jsp"></decorator>
    </decorators>
  5. Place the decorator JSP files referenced within the previous step into the WEB-INF/decorators directory. Then use the SiteMesh custom tags within these JSP files to apply the decoration.

    Example 1. Example my-html-decorator.jsp
    <%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %>
    <!DOCTYPE html>
    <html>
    <head>
        <title>Demonstration HTML format decorator</title>
        <decorator:head />
        <script>
        // Custom javascript here.
        </script>
    </head>
    <body>
        <header><!-- Custom header here --></header>
        <decorator:body />
        <footer><!-- Custom footer here --></footer>
    </body>
    </html>
                    

    Example 2. Example my-map-decorator.jsp

    This decorator demonstrates the use of the mapConfig JavaScript variable to customize the Leaflet Mapper.

    <%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %>
    <!DOCTYPE html>
    <html>
    <head>
        <title>Demonstration Map format decorator</title>
        <decorator:head />
        <script>
            var baseLayers = [];
            baseLayers['ESRI World Topo'] = 
                L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}', {
                attribution: 'Tiles &copy; Esri &mdash; Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community'
                ,maxZoom:18
            });
            baseLayers['Open Street Map'] = 
                L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors'
                ,maxZoom: 18
            });
            
            function initializeMap(map,geojsonLayer){
                L.control.layers(baseLayers, {'Student residences':geojsonLayer}).addTo(map);
            }
    
            var mapConfig = {
                options: {
                    layers:[baseLayers['Open Street Map']]
                }
                ,initCallback:initializeMap
                ,geoJsonOptions: {
                    // Customize the Leaflet popup.
                    onEachFeature:function(feature,layer){
                        layer.bindPopup(function(){
                            return '<h3>' + feature.properties['Some Property'] + '</h3>';
                        });
                    } 
                    // Customize the GeoJSON style
                    ,style:{"color": "#FA8072",
                        "weight": 5,
                        "opacity": 0.8
                    }
                }
            };
        </script>
    </head>
    <body>
        <header><!-- Custom header here --></header>
        <decorator:body />
        <footer><!-- Custom footer here --></footer>
    </body>
    </html>
                    

After performing these steps, a request to [/context]/pullreports/catalog/my-catalog/report/my-report/export?format=html will be decorated with the my-html-decorator.jsp decorator and a request to [/context]/pullreports/catalog/my-catalog/report/my-report/export?format=map will be decorated with the my-map-decorator.jsp decorator.

For more information about configuring SiteMesh, see the SiteMesh documentation.