Skip to content
PDF

This guide introduces the basic functionalities related to the Module Suite Smart Pages.

Basic concepts

The Module Suite Smart Pages is a Module Suite component that introduces new features for those users that need an extra level of flexibility when creating customized SmartUI perspectives, and, more broadly, for those who prefer using the SmartUI in place of the Classic UI for their Content Server applications.

The extension includes the following components:

  • A new set of SmartUI tiles, available within the Perspective Builder Widget Library
  • A set of Content Script snippets that showcase how to create datasources for the SmartUI tiles
  • Smart Pages module. Smart Pages module that aims to simplify the creation of good-looking functional user interfaces, both as a standalone solution and as part of the Smart View perspectives.
  • Low-coding Smart View tailoring capabilities (allows you to custoize several aspect of the Smart View without having to relay on the Smart View SDK and without the need to deploy new artifact on Content Server servers)

Module Suite Tiles in the Widget Library

Widget Library

The following tiles are available in AnswerModules Module Suite section:

  • Content Script Result
  • Content Script Tile Chart
  • Content Script Tile Links
  • Content Script Tile Tree
  • Content Script Node Table
  • Content Script Tile News
  • Content Script Tile Tiles

Configuration

Module Suite tiles share some common configuration options, while other options are specific to single tiles.

Common options include the configuration of the external frame (header, scrolling content, title, icon) and the configuration of the tile's Data Source. All Module Suite tiles require to specify a Content Script object that will be executed when the tile content is created. This script acts as a Data Source for the tile, and allows to make its content dynamic.

Through the configuration, it is also possible to pass additional parameters to the script. The parameter will be available to the developer within the params variable.

When configuring the tile's icon, two different approaches are possible:

  • specify a CSS style class to apply to the icon element. This should define the rules needed to apply the desired icon.
  • specify the name (and color scheme) of the desired icon among the ones available in the Module Suite icon set. See the icon reference cheat sheet for a full list of options.

Content Script Tile configuration

Dynamic Configuration

Module Suite tiles are designed to dynamically load their configuration from the same data source that supplies their data. This process is initiated by invoking the data source prior to the tile's rendering. To identify requests for configuration-only data, the widgetConfig parameter is used. This parameter signals the data source that it's being called specifically for tile configuration.

Considerations for Dynamic Configuration:
  • Flexibility vs. Initial Load Time: While this feature offers increased flexibility, it does come with the trade-off of additional loading time for the initial data source call.
  • Optimized Data Source Responses: It's advisable to configure the data source in a way that recognizes when it's being called solely for configuration purposes. Implementing strategies such as caching mechanisms or the use of static data can significantly expedite the configuration delivery.
Configuring Dynamic Loading:

To enable or disable dynamic configuration loading for a pane, use the common configuration option:

Configuring Dynamic Loading

List of Tiles

Content Script Result

The Content Script Result is a general-purpose tile that can be used to inject any output generated by a Content Script Data source or a Smart Page into a SmartUI perspective.

Content Script Result

Content Script Tile Chart

The Content Script Tile Chart is a tile who's purpose is to create interactive charts within the SmartUI. The data shown in the charts will be provided by a Content Script data source.

Chart tiles leverage two different javascript libraries:

  • Chartist (supported for backward compatibility)
  • Chart.js (suggested)

Depending on the selected chart type, the appropriate configuration has to be provided in JSON format.

Tile Chart

def rand = new Random()

if(params.widgetConfig){

    json(widgetConfig:[
        reloadCommands:["updateChart"],
        html:"""


<small>Move the mouse over the chart for triggering data-reload</small>
<script>
    csui.onReady2([ 
        "csui/lib/jquery", 
        "csui/lib/underscore", 
        "csui/lib/radio"], 
                function(jQ, _, Radio){

                    //Get the page message bus
                    var amChannel  = Radio.channel('ampagenotify');

                    //Get the chart
                    var chart = amChannel.request("ampages:myChart");

                    var canvas = jQ("#myChart");
                    canvas.unbind("click");
                    canvas.on("click", function (evt) {
                        var activePoints = chart.getElementsAtEvent(evt);
                        var vals = _.map(_.pluck(_.filter(chart.legend.legendItems, function(it){ return it.hidden==false}), "text"), function(value){ return value;}).join("|");
                        if(!_.isUndefined(activePoints[0])){
                        var chartData = activePoints[0]['_chart'].config.data;
                        var idx = activePoints[0]['_index'];

                        var label = chartData.labels[idx];
                        var value = chartData.datasets[0].data[idx];     
                        amChannel.trigger("updateChart", [ {name:"where_type", value:label} ]);

                    } else {
                        amChannel.trigger("updateChart", [ {name:"where_type", value:vals} ]);
                    }
                    });


                    canvas.hover(function(){
                        var self = jQ(this);
                        //jQ(".myChartLoader").removeClass("binf-hidden");
                        amChannel.trigger("updateChart", [{name:"filter", value:"first"}]);
                    });
                });
</script>"""
    ])
}else{

    json([

        type:"bar",
        data: 
        [

            labels: ["red", "green"],
            datasets: [

                [
                    label: "My First dataset",
                    backgroundColor: "${AMBWFWidgetsLib.getBehaviour("ambwf","generateRandomHTMLColor", this)(rand)}",
                    borderColor: "${AMBWFWidgetsLib.getBehaviour("ambwf","generateRandomHTMLColor", this)(rand)}",
                    data: [rand.nextInt(100), rand.nextInt(100)],

                ],
                [
                    label: "My Second dataset",
                    borderColor: "${AMBWFWidgetsLib.getBehaviour("ambwf","generateRandomHTMLColor", this)(rand)}",
                    backgroundColor: "${AMBWFWidgetsLib.getBehaviour("ambwf","generateRandomHTMLColor", this)(rand)}",
                    data: [ rand.nextInt(100), rand.nextInt(100)],

                ]
            ]
        ],
        options: [
            maintainAspectRatio: false,
            title: [
                display: true,
                text: 'myChart',
                position: 'left'
            ],
            legend: [
                display: true,
                position: 'top'
            ],
            scales: [
                yAxes: [
                    [
                        ticks: [
                            beginAtZero:true
                        ]
                    ]
                ]
            ]
        ]
    ])
}

Content Script Tile Tiles

The Content Script Tile Tiles is a tile meant to create a customizable list of clickable links and HTML Tiles. The data controlling the links is provided by the backing Content Script data source.

Tile Links

    app = runCS("am_businessCompliance")
    if( params.widgetConfig ){
        json( widgetConfig : [
            reloadCommands : [ "updateTiles" ], // The widget will be refreshed when this command is executed
            html : """
                <link type="text/css" rel="stylesheet" data-csui-required="true" href="${app.config.static.tilesCss}">
                <script>
                csui.onReady2([
                    'csui/lib/jquery',
                    'csui/lib/underscore',
                    'csui/lib/marionette',
                    'csui/lib/radio',
                    'csui/utils/commands',
                    'csui/controls/side.panel/side.panel.view',
                    'csui/controls/tile/behaviors/perfect.scrolling.behavior',
                    'anscontentsmartui/utils/contexts/factories/scriptjsonresult.model.factory'
                    ],
                    function (jQ, _, Marionette, Radio, CommandsRegistry, SidePanelView, PerfectScrollingBehavior, ContentScriptModelFactory) {
                        var ContentView = Marionette.View.extend({
                            constructor: function ContentView(options) {
                                this.widgetConfig = options.widgetConfig || {};
                                this.options = options;
                                Marionette.View.prototype.constructor.apply(this, arguments);
                            },
                            className: 'anscontentsmartui-tile-content-script',
                            render: function () {
                                var source;
                                if (this.model) {
                                    source = this.model.get('cssource');
                                    if (!_.isUndefined(source)) {
                                        var self = this;
                                        csui.require(['csui/lib/jquery'], function (jQ) {
                                            jQ(self.\$el).html(source);
                                        });
                                    }
                                }
                                return this;
                            },
                            className: 'amsui-exp-content-script',
                            behaviors: {
                                PerfectScrolling: {
                                    behaviorClass: PerfectScrollingBehavior,
                                    contentParent: ".am-smartui",
                                    suppressScrollX: true,
                                    scrollYMarginOffset: 15,
                                    scrollingDisabled: false
                                }
                            }
                        });

                        // Get the page message bus
                        var amChannel = Radio.channel('ampagenotify');
                        amChannel.off("tiles_action");
                        amChannel.on("tiles_action", function (action, param) { //action = panel|1234|My Title|80
                            if( action.startsWith('panel') ){
                                var scriptID   = undefined;
                                var title      = "Action Panel";
                                var panelWidth = 80;
                                var params     = undefined; 
                                if( action.includes("|") ){
                                    var tokens = action.split('|')
                                    if( tokens.length >= 2){
                                        if(jQ.isNumeric( tokens[1] )){ //panel|1234...
                                            scriptID = tokens[1];
                                            params = param;
                                            if( tokens.length >= 3){
                                                if(jQ.isNumeric( tokens[2] )){ //panel|1234|80
                                                    panelWidth = tokens[2];         
                                                }else{ //panel|1234|My Title
                                                    title = _.escape(tokens[2]);
                                                    if( tokens.length >= 4){
                                                        if(jQ.isNumeric( tokens[3] )){ //panel|1234|My Title|80
                                                            panelWidth = tokens[3];
                                                        }
                                                    }
                                                }
                                            }
                                        }else{
                                            //panel|My title...
                                            title = _.escape(tokens[1]);
                                            if( tokens.length >= 3){ 
                                                if(jQ.isNumeric( tokens[2] )){ //panel|My Title|80
                                                    panelWidth = tokens[2];         
                                                }
                                            }
                                        }
                                    }
                                    if( scriptID === undefined ){
                                        scriptID = param;
                                    }
                                } 

                                if( jQ.isNumeric( scriptID ) ){
                                    var context = amChannel.request("ampages:pageContext");
                                    var scriptAttrs = { source: scriptID };
                                    var script = context.getModel(ContentScriptModelFactory, { attributes: scriptAttrs });
                                    if(params != undefined){
                                        script.attributes.parameters = [{ name: "actionParams", value: params }];
                                    }
                                    var slides = [
                                        {
                                            title   : title,
                                            content : new ContentView({ model: script })
                                        }
                                    ];
                                    script.fetch().then(function () {
                                        var dialog = new SidePanelView(_.extend({
                                            sidePanelClassName : "amsui-smartui-slide-panel-"+panelWidth+"vw",
                                            openFrom           : "right",
                                            slides             : slides
                                        }));

                                        dialog.show();
                                        amChannel.on("tiles_panel:hide", function () {
                                                dialog.hide();
                                        });
                                    });
                                } else {
                                    console.log("Error opening panel - invalid settings.")
                                }
                            };
                        });
                    });
                </script>
                <style>
                .binf-widgets [data-csui-widget_type=tilelinkstiles_content_script] .am-tile-content{
                    padding-right: 15px;
                }
                </style>
                """ ])
    } else {

        json(
            data : [
                styleclass : "myStyleClass",
                rows : [
                    [   // First row
                        styleclass : "",
                        size : 1, // The relative height of this row compared to other rows (default : 1)
                        tiles : [
                            [ // First Tile
                                size       : 1, // The relative size of this tile compared to others in the row (default : 1)
                                styleclass : "",
                                html : """<div class="showcase-tiles-heading"> 
                                        <div class="showcase-tiles-heading-main">Third party due diligence</div>
                                        <div style="font-size: 0.6em;">Process to manage the engagement, monitoring and payment of third parties</div>
                                        </div>
                                        """
                            ]
                        ]
                    ],
                    [   //Empty Row
                        styleclass : "myStyleClass",
                        size : 1, // The relative height of this row compared to other rows (default : 1)
                        tiles : [
                            [ // First Tile
                                size       : 1, // The relative size of this tile compared to others in the row (default : 1)
                                styleclass : "myStyleClass",
                                html : """<div class="showcase-tiles-section"></div>"""
                            ]
                        ]
                    ],
                    [  
                        styleclass : "",
                        size : 1, // The relative height of this row compared to other rows (default : 1)
                        tiles : [
                            [ 
                                size       : 1, // The relative size of this tile compared to others in the row (default : 1)
                                styleclass : "",
                                html : """<div class="showcase-tiles-section" style="text-align: left; padding-left: 18px; font-size: 1em;">
                                            The Business Compliance process has been implemented as a case management application.
                                            It is intended to demonstrate how it is possible to manage a Business Compliance process by 
                                            integrating a Connected Workspace, representing a third party, with a list of tasks for managing the
                                            different steps such as assessment, engagement, monitoring and payment of the above, in accordance with
                                            internal regulatory requirements.
                                        </div>"""
                            ]
                        ]
                    ],
                    [
                        size : 3,
                        tiles : [
                            [   // First Tile
                                size : 1,
                                type : 'red', // Available types: red, green, blue, orange, teal, gold, purple
                                front : [
                                    icon : "${img}anscontentsmartui/app/image/icons/windows10/white/risk_assesment.svg",
                                    body : """3""",
                                    body_text_align : 'right', // left, center, right (default)
                                    body_text_size : 'jumbo', // small (90%), normal (100%), large (200%), jumbo (300%)
                                    title : "Late Tasks"
                                ],
                                back : [
                                    icon : "${img}anscontentsmartui/app/image/icons/windows10/white/task.svg",
                                    title : "Late Tasks",
                                    body_text_align : 'center',
                                    body : """Potuit, iam districtum mucronem in proprium latus inpegit. hocque deformi genere mortis excessit e vita iustissimus 
                                            rector ausus miserabiles casus levare multorum. hinc ille commotus ut iniusta perferens et indigna praefecti 
                                            custodiam protectoribus mandaverat fidis."""
                                ]
                            ],
                            [   // Second Tile
                                size : 1,
                                type : 'green', // Available types: red, green, blue, orange, teal, gold, purple
                                front : [
                                    icon : "${img}anscontentsmartui/app/image/icons/windows10/white/public_private_company.svg",
                                    body : """9""",
                                    body_text_align : 'right', // left, center, right (default)
                                    body_text_size : 'jumbo', // small (90%), normal (100%), large (200%), jumbo (300%)
                                    title : "Active Processes"
                                ],
                                back : [
                                    icon : "${img}anscontentsmartui/app/image/icons/windows10/white/task.svg",
                                    title : "Active Processes",
                                    body_text_align : 'center',
                                    body : """<table class="binf-table binf-table-condensed">
                                                <thead>
                                                    <tr>
                                                        <th>First Col</th>
                                                        <th>Second Col</th>
                                                    </tr>
                                                </thead>
                                                <tbody>
                                                    <tr>
                                                        <td>Some Data</td>
                                                        <td>Other Data</td>
                                                    </tr>
                                                </tbody>
                                            </table>"""
                                ]
                            ],
                        ]
                    ],
                    [
                        size : 3,
                        tiles : [
                            [   
                                size : 1,
                                type : 'teal', // Available types: red, green, blue, orange, teal, gold, purple
                                front : [
                                    icon : "${img}anscontentsmartui/app/image/icons/windows10/white/flag.svg",
                                    body : """254""",
                                    body_text_align : 'right', // left, center, right (default)
                                    body_text_size : 'large', // small (90%), normal (100%), large (200%), jumbo (300%)
                                    title : "Registered Third Parties"
                                ],
                                back : [
                                    icon : "${img}anscontentsmartui/app/image/icons/windows10/white/flag.svg",
                                    title : "Registered Third Parties"
                                ]
                            ],
                            [   
                                size : 1,
                                type : 'orange', // Available types: red, green, blue, orange, teal, gold, purple
                                front : [
                                    icon : "${img}anscontentsmartui/app/image/icons/windows10/white/task.svg",
                                    body : """42""",
                                    body_text_align : 'right', // left, center, right (default)
                                    body_text_size : 'jumbo', // small (90%), normal (100%), large (200%), jumbo (300%)
                                    title : "Open Tasks"
                                ],
                                back : [
                                    icon : "${img}anscontentsmartui/app/image/icons/windows10/white/task.svg",
                                    title : "Open Tasks"
                                ]
                            ],
                        ]
                    ],
                    [   
                        styleclass : "myStyleClass",
                        size : 1, // The relative height of this row compared to other rows (default : 1)
                        tiles : [
                            [ // First Tile
                                size       : 1, // The relative size of this tile compared to others in the row (default : 1)
                                styleclass : "myStyleClass",
                                html : """<div class="showcase-tiles-section"> Actions </div>"""
                            ]
                        ]
                    ],
                    [
                        size : 0,
                        tiles : [ 
                            [
                                size : 12,
                                styleclass : "",
                                command : "tiles", // Custom command
                                action : "panel|Register New Third-Party|60", 
                                params : app.config.pages.caseNew, //The action's parameter
                                newtab : false,
                                type : 'green',
                                front : [
                                    icon : "${app.config.static.resourcesPath}add.svg",
                                    body : "Start Business Compliance Process",
                                ]
                            ]
                        ]
                    ],
                    [
                        size : 0,
                        tiles : [ 
                            [
                                size : 12,
                                styleclass : "",
                                command : "cases", // Custom command
                                action : "z_changeMode", 
                                params : "grid", //The action's parameter
                                newtab : false,
                                type : 'green',
                                front : [
                                    icon : "${img}anscontentsmartui/app/image/icons/windows10/white/report2.svg",
                                    body : "Business Compliance List",
                                    //title : "Analytics"
                                ]
                            ]
                        ]
                    ],
                    [
                        size : 0,
                        tiles : [ 
                            [
                                size : 12,
                                styleclass : "",
                                command : "cases", // Custom command
                                action : "z_changeMode", 
                                params : "stats", //The action's parameter
                                newtab : false,
                                type : 'green',
                                front : [
                                    icon : "${img}anscontentsmartui/app/image/icons/windows10/white/stats_line_chart.svg",
                                    body : "Analytics",
                                    //title : "Analytics"
                                ]
                            ]
                        ]
                    ],
                    [
                        size : 0,
                        tiles : [ 
                            [
                                size : 12,
                                styleclass : "",
                                command : "cases", // Custom command
                                action : "z_changeMode", 
                                params : "kaban", //The action's parameter
                                newtab : false,
                                type : 'green',
                                front : [
                                    icon : "${img}anscontentsmartui/app/image/icons/windows10/white/diversity.svg",
                                    body : "Business Compliance By Status",
                                ]
                            ]
                        ]
                    ],
                    [
                        size : 0,
                        tiles : [ 
                            [
                                size : 12,
                                styleclass : "",
                                command : "cases", // Custom command
                                action : "z_changeMode", 
                                params : "conf", //The action's parameter
                                newtab : false,
                                type : 'green',
                                front : [
                                    icon : "${app.config.static.resourcesPath}settings.svg",
                                    body : "Configuration",
                                ]
                            ]
                        ]
                    ],
                    //Empty
                    [   
                        styleclass : "myStyleClass",
                        size : 1, // The relative height of this row compared to other rows (default : 1)
                        tiles : [
                            [ 
                                size       : 1, // The relative size of this tile compared to others in the row (default : 1)
                                styleclass : "myStyleClass",
                                html : """<div class="showcase-tiles-section"></div>"""
                            ]
                        ]
                    ],
                ]
            ]
        )
    }

The Content Script Tile Links is a tile meant to create a customizable list of clickable links. The data controlling the links is provided by the backing Content Script data source.

Tile Links

    if(params.widgetConfig){

        json(widgetConfig:[
            reloadCommands:["updateLinks"],
            html:"""

    <style>
    div.ans-tile-content-linkstiles{
        background: linear-gradient(180deg, #122c69 0%, #078db3 100% );
        color:#fff;
        height:100%;
    }

    div.ans-tile-content-linkstiles > div.binf-list-group > a:nth-child(2),
    div.ans-tile-content-linkstiles > div.binf-list-group > a:nth-child(6),
    div.ans-tile-content-linkstiles > div.binf-list-group > a:nth-child(10){
        background:#00639b;
        color:#fff;
        border-radius:0px;
    }
    div.ans-tile-content-linkstiles > div.binf-list-group > a:nth-child(3),
    div.ans-tile-content-linkstiles > div.binf-list-group > a:nth-child(7),
    div.ans-tile-content-linkstiles > div.binf-list-group > a:nth-child(11){
        background:#df3324;
        color:#fff;
        border-radius:0px;
    }
    div.ans-tile-content-linkstiles > div.binf-list-group > a:nth-child(4),
    div.ans-tile-content-linkstiles > div.binf-list-group > a:nth-child(8),
    div.ans-tile-content-linkstiles > div.binf-list-group > a:nth-child(12){
        background:#008485;
        color:#fff;
        border-radius:0px;
    }

    </style>
    <div style="padding:20px; background-color:white;margin-bottom:10px;color:#333" >
    Click on the differnt links to see them in action.
    </div>
    <script>
        csui.onReady2([    'csui/lib/underscore',
            'csui/lib/backbone',
            'csui/lib/jquery',
            'csui/lib/radio'], 
            function(_,Backbone, jQ, Radio){
                var amChannel = Radio.channel("ampagenotify");
                amChannel.on("smartPage_action", function(action,param){
                    console.log("GOT Page Action request. Action: "+action+ " parameter: "+param);
                });

            });
    </script>
    """
        ])
    }else{

        retVal = 
            [
                data:[
                    links:[
                        [
                            issection:true,
                            name:"First Section",
                        ],
                        [
                            issection:false,
                            icon:"csui-icon-home",
                            name:"First Link (Navigate)",
                            desc:"More information for this link",

                            url:"#", //If action != null url must be set equal to #
                            action:"navigate", //Will trigger a browse action of the current view
                            params:"2000", //The DataID of the node you wanto to navigate to 

                        ],
                        [
                            issection:false,
                            icon:"icon-tileExpand icon-perspective-open",
                            name:"Duplicate (Action)",

                            url:"#", //If action != null url must be set equal to #
                            action:"notify", //Will trigger the execution of the command below
                            command:"updateLinks", //The action to execute
                            params:"duplicate", //The action's parameter, this value will be passed to the script in a parameter named "tile"

                        ],
                        [
                            issection:false,
                            icon:"icon-socialFavOpen",
                            name:"Notify Smart Page (Page Action)",

                            url:"#", //If action != null url must be set equal to #
                            command:"smartPage", //The SmartPage(s) to notify
                            action:"updatePage",   //The action to execute
                            params:"2000" //The action's parameter

                        ],
                        [
                            issection:false,
                            am_icon:"am_icon_link",
                            am_icon_schema:"am_icon_green",
                            name:"Simple link",

                            url:"http://www.answermodules.com",
                            newtab:true

                        ]

                    ]
                ]
            ]

        if(params.tile == "duplicate"){
            retVal.data.links += retVal.data.links[-5].clone()
            retVal.data.links += retVal.data.links[-5]
            retVal.data.links += retVal.data.links[-5]
            retVal.data.links += retVal.data.links[-5]

            retVal.data.links[-4].name = "Second Section"
        }else if(params.tile == "triple"){
            retVal.data.links += retVal.data.links[-5].clone()
            retVal.data.links += retVal.data.links[-5]
            retVal.data.links += retVal.data.links[-5]
            retVal.data.links += retVal.data.links[-5]

            retVal.data.links[-4].name = "Second Section"

            retVal.data.links += retVal.data.links[-4].clone()
            retVal.data.links += retVal.data.links[-4]
            retVal.data.links += retVal.data.links[-4]
            retVal.data.links += retVal.data.links[-4]

            retVal.data.links[-4].name = "Third Section"
        }

        json(
            retVal
        )


    }

Content Script Tile Tree

The Content Script Tile Tree creates an interactive tree structure with nodes that can be expanded and collapsed. The tree structure uses a Content Script data source for the initial data and for subsequent ajax data load calls.

Tile Tree

    if(params.widgetConfig){
        json( [ id           : 2,
            widgetConfig : [
                tileLayoutClasses  : "", 
                tileContentClasses : "", 
                reloadCommands     : ["updateTree"],
                root               : 2000, 
                plugins            : [ "wholerow" ],
                theme              : [ 'name': 'proton',
                                        'responsive': true ],
                html               : """
    <style>
    div.ans-tile-tree{
    background: linear-gradient(180deg, #122c69 0%, #078db3 100% );
    color:#fff;
    height:calc(100vh - 222px);
    font-size:13px !important;
    }
    .binf-widgets .jstree-proton .jstree-icon.csui-icon-node-task {
    background-image:url('${img}csui/themes/carbonfiber/image/icons/mime_task.svg')
    }
    .binf-widgets .jstree-proton .jstree-icon.mime_pdf{
    background-image:url('${img}csui/themes/carbonfiber/image/icons/mime_pdf.svg')
    }
    .jstree-anchor small{
    font-size:.9em;
    font-style:italic;
    }
    </style>
    <div class="am-form-text-input" style="margin-top: 1px;padding: 5px 0px;">
        <label class=" control-label col-form-label am-form-text-input-label   am-form-label-top" style="padding: 5px;">Filter tree</label>
        <div class="am-form-input-wrap" style="padding: 0 5px;">
            <input id="filter" type="text" placeholder="" class="form-control" style="border-radius: 0px;box-shadow: none;">
        </div>
    </div>

    <script>
        csui.onReady2([    'csui/lib/underscore',
            'csui/lib/backbone',
            'csui/lib/jquery',
            'csui/lib/radio'], 
            function(_,Backbone, jQ, Radio){
                var amChannel = Radio.channel("ampagenotify");
                amChannel.on("printConsole", function(params){
                    console.log("GOT request "+JSON.stringify(params));
                });
                amChannel.on("smartPage_action", function(action,param){
                    console.log("GOT Page Action request. Action: "+action+ " parameter: "+param);
                });
                jQ("#filter").on("blur", function(){
                    amChannel.trigger("updateTree",{'term':jQ(this).val()})
                })  

            });
    </script>"""
            ]
            ] )
        return
    }

    data =

        [
            [

                icon     : "csui-icon cs_vfolder", //mime_folder, cs_folder_root, cs_vfolder, cs_folder_open...
                id       : 1,
                text     : "Roots",
                children :  [
                    [
                        action   : "navigate", //Trigger a Smart View navigation
                        icon     : "csui-icon cs_folder_root", //cs_folder_root, cs_vfolder, cs_folder_open
                        id       : 2000, //The node will be used as the action's parameter
                        text     : "Home",
                        children : false
                    ]

                ],
                state       : [
                    opened    : true
                ] 
            ],
            [

                action   : "printConsole", //Trigger a Tile action
                params   : "3", //This value will be passed to the script in a parameter named 'tile'
                icon     : "csui-icon mime_folder",
                id       : 3,
                text     : "Folder (Lazy Loaded)",
                children : true,
                state       : [
                    opened    : false
                ] 
            ]
        ]


    if(params.uiParentID == "3"){
        data[1].children = [
            [

                icon     : "csui-icon mime_folder",
                id       : 4,
                text     : "Sub Folder",
                children : [
                    [
                        notify   : "smartPage",    //Triggers a Smart Page action noifying the provided page(s) (CSV)
                        action   : "customAction", //The action to execute
                        params   : "2000",         //The action's parameter
                        icon     : "csui-icon mime_pdf",
                        id       : 5,
                        text     : "Notify Smart Page",
                        children : false
                    ],
                    [
                        action   : "printConsole",
                        params   : "2000",
                        icon     : "csui-icon mime_pdf",
                        id       : 6,
                        text     : "Execute Action",
                        children : false
                    ]

                ]
            ]

        ]
    }
    if(params.term){
        data = data.findAll{it.text.startsWith(params.term)}
    }
    json(data)

Content Script Node Table

The Content Script Node Table is an enhancement of the standard Node Table tile. The tile uses a Content Script as data source, allowing to set up any custom business logic to generate the list of nodes to be shown.

Tile Node Table

    def targetSpaceFilter = 2000


    def subtypeFilter = "144".split(",") 

    if(params.widgetConfig){
        json([
        widgetConfig:[
            reloadCommands:[ "updateData" ],
            columnsWithSearch:[ "Owner", "Name" ]
        ]
        ])
        return 
    }

    if(params.page?.contains("_") && params.page_list){
        if(params.page_list[0].contains("_") && !params.page_list?[1]?.contains("_")){
            params.page = params.page_list[1]
        }else if(!params.page_list[0].contains("_") && params.page_list?[1]?.contains("_")){
            params.page = params.page_list[0]
        }
    }

    def paging = [actual_count:0, 
                limit:((params.limit?:"30") as int), 
                page:((params.page?:"1") as int), 
                page_total:0, 
                range_max:0, 
                range_min:0, 
                total_count:0, 
                total_row_count:0, 
                total_source_count:0]

    def pageSize = paging.limit
    def offset = (paging.limit * (paging.page - 1))
    def firstRow = offset + 1
    def lastRow = firstRow + paging.limit

    nodes = []

    def nameFilter = null
    if( params.where_name ){
        nameFilter = "%${params.where_name}%"
    }

    def ownerFilter = null
    if( params.where_owner ){
        ownerFilter = "%${params.where_owner}%"
    }

    def sortingOrderParam     = 'desc'
    def sortingColumnParam     = 'name'

    def sortingOrder     = 'DESC'
    def sortingColumn     = 'DTree.Name'


    if( params.sort && params.sort.contains('_') ){

        def sorting = params.sort.split('_')

        sortingOrderParam     = sorting[0]
        sortingColumnParam     = sorting[1]

        sortingOrder = ( sortingOrderParam == 'asc' ) ? 'ASC' : 'DESC'


        switch( sortingColumnParam?.trim() ){

            case 'name' :
                sortingColumn = 'DTree.Name'
                break

            case 'owner' :
                sortingColumn = 'KUAF.ID'
                break

            default :
                sortingColumn = 'DTree.Name'
                break
        }

    }


    try{


        def queryParams = [targetSpaceFilter as String]
        def queryIndex = 1

        def permExpr = "(exists (select DataID from DTreeACL aclT where aclT.DataID=DTree.DataID and ${users.getRightsStringForSQL("RightID", false)} and See >1 ))"


        sqlCode = """ select DTree.DataID "DID", 
                            DTree.Name "NAME", 
                            COUNT(*) OVER() as "overall_count" 

                        from DTree 
                        LEFT JOIN KUAF ON DTree.UserID = KUAF.ID

                        where DTree.ParentID = %1 """

        if(subtypeFilter.size() == 1){
            sqlCode += " and DTree.SubType = %${++queryIndex} "
            queryParams << (subtypeFilter[0] as long)
        } else if( subtypeFilter.size() > 1 ) {
            sqlCode += " and DTree.SubType IN (${subtypeFilter.join(',')}) "
        }

        if(nameFilter){
            sqlCode += " and DTree.Name LIKE %${++queryIndex} "
            queryParams << (nameFilter as String)
        }

        if(ownerFilter){
            sqlCode += " and (KUAF.Name LIKE %${++queryIndex} OR KUAF.LastName LIKE %${queryIndex} ) "
            queryParams << (ownerFilter as String)
        }

        if(!users.current.canAdministerSystem){
            sqlCode += " and ${permExpr} "
        }

        sqlCode += """
                    ORDER BY ${sortingColumn} ${sortingOrder}
                        OFFSET ${offset} ROWS
                        FETCH NEXT ${pageSize} ROWS ONLY

                    """



        def queryResults

        if(queryParams){
            queryResults =  sql.runSQLFast(sqlCode, true, true, 100, *queryParams).rows
        } else {
            queryResults =  sql.runSQLFast(sqlCode, true, true, 100 ).rows
        }   

        def totalCount = (queryResults) ? queryResults[0].overall_count : 0

        nodes = queryResults?.collect{it.DID as Long}



        paging << [
                actual_count:totalCount, 
                page_total:((totalCount%paging.limit)+1),
                range_min:paging.page*paging.limit-paging.limit+1,
                range_max:(paging.limit*(paging.page+1)-totalCount)>0?(paging.limit*(paging.page+1)-totalCount):paging.limit*(paging.page+1),
                total_count:totalCount, 
                total_row_count:totalCount, 
                total_source_count:totalCount]

    }catch(e){
        log.error("Error loading nodes table data",e)
        printError(e)
    }



    def drawStatusBar = { node ->

        def statusList = ['Draft', 'Under Revision', 'Approved', 'Published']
        def numSteps = statusList.size()
        def currStep = new Random().nextInt(statusList.size())
        def currStepName = statusList[currStep]


        def stepStyle = "height:100%; width:calc(100% / ${numSteps}); float:left; background-color:#F0AD4E; box-sizing:border-box;"

        def stepsHtml = ""

        (currStep + 1).times{
            stepsHtml += """<span style="${stepStyle}"></span>"""
        }


        return """ 
        <div style="text-align:center; font-size:.75em">${currStepName}</div>
        <div style="margin:3px 0; padding:0; height:5px; background-color:#eee;">${stepsHtml}</div>"""
    }



    def slurper = new JsonSlurper()

    def processNode = { node, myNode ->

        /* Add your custom node post-processing here */

        //def myNode = asCSNode(node?.data.properties.id as long)

        node.data.amcsproxy =  [
            columns: [:],
            commands:[] 
        ]

        //Add custom column: node.data.amcsproxy.colums.sample_column = "My custom Value"

            def owner = myNode.createdBy
            def ownerBox = "<span><img src='/otcs/cs.exe/pulse/photos/userphoto/${owner.ID}/2000' style='max-height: 3em; border-radius: 50%; margin-right: 5px; vertical-align: middle;' /> ${owner.displayName}</span>"
            node.data.amcsproxy.columns.owner = ownerBox

        node.data.amcsproxy.columns.comment = myNode.comment 
        node.data.amcsproxy.columns.statusBar = drawStatusBar( myNode ) 

        return node
    }

    results = []

    def fields = JsonOutput.toJson( [
        'actions': [ 'fields': [] ],
        'properties': [ 'fields': [] ],
        'versions': [ 'fields': [] ],
        'amcsproxy': [ 'fields': [] ],
    ])

    //Identifies actions to be displayed for every node
    //Node actions are return together with data request thay may lead to additinal response time
    // [] - docman.getNodesRestV2JSon will not process actions. 
    //      Actions will be processed on a separate call based on the list provided (see returned json object at the end of this script)
    // null - default list of actions will be returned
    // ['open','properties','copy','move','edit'] - sample list of actions
    // To ideal actions processing requires you to assign an empty list (see below) to the nodesActions variable below and pass the list of commands to be retrived
    // using the 'actions' list property of the json object returned by this script (see last line)
    def nodesActions = [] 

    if( nodes.size() > 1 ){
        log.error("Nodes ${nodes}")
        temp = slurper.parseText( docman.getNodesRestV2JSon(nodes, fields, '{"properties":{"fields":["parent_id"]}}', false, false, nodesActions) )
        theNodes = docman.getNodesFastWith(nodes, [], params, false, false, false)
        nodes.each{ node ->

            def jsonNode = temp.find{ it.data.properties.id == node }
            results << processNode(jsonNode, theNodes.find{it.ID == node}  )
        }


    } else if (nodes.size() == 1 ){

        it = slurper.parseText(docman.getNodesRestV2JSon(nodes, fields, '{"properties":{"fields":["parent_id"]}}', false, false, nodesActions))
        processNode(it, docman.getNodeFast(nodes[0])) 

        results = [it]
    }




    def columns = [

        type: [
                key:"type",
                name:"Type",
                type:2,
                type_name:"Integer",
                sort:false
            ]

        ,name: [
                key:"name",
                name:"Name",
                type:-1,
                type_name:"String",
                sort:true,
                align:"left"
            ]

        ,owner: [
                key:"owner",
                name:"Owner",
                type:43200,
                type_name:"String",
                sort:true,
                align:"left"
            ]

        ,statusBar: [
                key:"statusBar",
                name:"Doc. Status",
                type:43200,
                type_name:"String",
                sort:false,
                align:"left"
            ]

        ,comment: [
                key:"comment",
                name:"Comment",
                type:-1,
                type_name:"String",
                sort:false,
                align:"left"
            ]
    ]


    // actions - list of commands defined for all the nodes listed in the page
    // action=[] - will return all possible actions for a node
    json(
        [
        paging:paging,
        columnsWithSearch:[ "name" , "owner" ], 
        results:results, 
        columns:columns, 
        tableColumns:columns,
        widgetConfig:[
            reloadCommands:[ "updateData" ]
        ],
        actions: ['open','properties','copy']
    ]
    )

Embedding Beautiful WebForms views in SmartUI

In order to embed a Beautiful WebForms form in a SmartUI tile, it is possible to use a Content Script Result Tile with the following minimal configuration:

def formID = 123456 // the dataID of the form to embed
def viewID = 234567 // the dataID of the SmartUI form view, within the Form Template

form = forms.getFormInfo(formID)
view = asCSNode(viewID)

json([ output : view.renderView(binding, form),     
       widgetConfig :[ 
           reloadCommands:[], // any SmartUI commands that will trigger a reload of the form
           tileContentClasses:"am-nobckg",
           tileLayoutClasses:"am-nobckg"
       ]
     ])   

Form View Template

In order for the form to load resources compatible with usage within the SmartUI, you should use the "SmartView Embeddable" form template, available within the SmartUI extension libraries.

For additional details, see the dedicated section in the Beautiful WebForms documentation.

Icon reference cheat sheet

Iconset Color codes

Module Suite icons are available in the following colors:

Icons colors

All icons

A complete list of the currently available icons is shown below: Icons reference

Smart Pages

Smart View overrides - general concepts

Like many other features in Module Suite, Smart Pages overrides of Smart View features follow a convention on the configuration approach so that for applying a customization to the Smart View UI using one of the supported overrides it is sufficient, in most cases, to create the appropriate script under the appropriate Content Script Volume folder. Smart View overrides are organized as follows:

  • Content Script Volume
  • CSSmartView
    • Actions Used to define lazy loaded actions to be displayed in nodes' related actionbars
    • Commands Used o define new commands to be displayed in nodes' related actionbars
    • Columns Used to define custom dynamic columns to be displayed in Content Server spaces
    • Overrides Ovverrides configuration. Its content determines when and where a particular override (above) is used

Having a possible serious impact on the end user experience, it is important that the system is effective in calculating how, where and when overrides should be applied. For this reason Module Suite uses an elaborate algorithm to determine the Actual Override Map (AOP) to use when overrides should be applied. The following is a detailed description of how the AOM is determined.

The content of the Overrides folder is used to compute an Override Map (OM), specific to your repository, having the following structure:

OM = [
    "globals": [            (1)
        540588              
    ],
    "type": [               (2)
        "144": [            (3)
            548066
        ]
    ],
    "tenants": [            (4)
        "497147": [         (5)
            "globals": [    (6)
                548169
            ],
            "type": [       (7)
                "144": [    (8)
                    496932
                ]
            ],
            "ids": [        (9)
                "496931": [ (10)
                    545972
                ]
            ]
        ]
    ]
]

where:

  • (1) identifies a list of scripts to be always executed
  • (2) a list of scripts to be executed only if the current space has at least one node having of the identified type (3)
  • (4) scripts to be considered only if the current space is descendant of the specified tenant (5) (a space identified by its DataID)
  • (5) is a "tenant" configuration
  • (6) identifies a list of scripts that must always be executed if the current space is descendant of the specified tenant (5)
  • (7) a list of scripts to be executed only if the current space has at least one node having of the identified type (8) and is descendant of the specified tenant (5)
  • (9) a list of scripts to be executed only if the current space has at least one node having of the identified id (10) and is descendant of the specified tenant (5)
  • scripts in the OM are executed in the following order (1), (2), (6), (7), (10).

Given the above example and imagining that all the scripts in (3) (8) and (10) return the list ["comm_one","comm_two"], the resulting AOM will contain:

(3) AOM = [
                ...
                "S144":[commands:["comm_one","comm_two"]],
                ...
            ]
    (8) AOM = [
                ...
                "S144":[commands:["comm_one","comm_two"]],
                ...
            ]
    (10) AOM = [
                ...
                "D496931":[commands:["comm_one","comm_two"]],
                ...
            ]
- scripts in (1), (6), (10) MUST return a Map having entries of the form:
    "SXXXX":[                                                                      
        commands:["comm_one", "comm_two",...],
        columns: [ //Optional                                                       
                    col_name:"col value", //value can be HTML
                    ...
                    ]
    ]
    where XXXX is a valid SubType
    or
    "DYYYY":[                                                                      
        commands:["comm_one", "comm_two",...],
        columns: [  //Optional                                                    
                    col_name:"col value", //value can be HTML
                    ...
                    ]
    ]

where YYYY is a valid node's ID.

OM is to be considered a "static" information in productive environments and as such, to guarantee optimal performances, the framework should be allowed to cached it.

This can be done by setting to "true" the " amcs.amsui.volumeCache" parameter int the base configuration.

When a user changes the current space, the OM is evaluated by the framework against the users' permissions and the actual override map (AOM) associated to the space is determined. AOM is determined by executing the relevant scripts in OM in the order described above. The AOM has the following form:

AOM = [
    "S144":[                                                                       (1)
            commands:["comm_one", "comm_two",...], //list of commands' command_key  (2)
            columns: [                                                             (3)
                        col_name:"col value", //value can be HTML
                        ...
                     ]
           ],
    "D1234":[                                                                      (4)
            commands:["comm_one", "comm_two",...], //list of commands' command_key
            columns: [
                        col_name:"col value", //value can be HTML
                        ...
                     ]
           ]
    ...
]

where: (1) represents commands and columns to be associated to all the nodes having the identified subtype, (3) can be omitted, (4) represents commands and columns to be associated a specific node (identified by its id), (4) takes precedence over (1).

How OM is created ?

In order to determine the OM, the content of the Overrides folder is evaluated following the logic below:

[
    "globals":[             (1)     
        540588
    ],
    "type": [               (2)
        "144": [            (3)
            548066          
        ]
    ],
    "tenants": [            (4)
        "497147": [         (5)
            "globals": [    (6)
                548169
            ],
            "type": [       (7)
                "144": [    (8)
                    496932
                ]
            },
            "ids": [        (9)
                "496931": [ (10)
                    545972
                ]
            ]
        ]
    ]
]
  • (1) Contains the list of scripts objects stored directly under "Overrides"
  • (2) For each direct subfolder of "Overrides" that has a name starting by the letter "S" an entry is created in "type" map (2). The key of such entry is the target subtype (as specified in the subfolder's name) while the value is the list of scripts contained the aforementioned subfolder.
  • (4) For each direct subfolder of "Overrides" that has a name starting by the letter "D" an entry is created in "tenants" map (2). The key of such entry is the tenant's DataID (as specified in the subfolder's name) while the value is the tenant OM configuration.
  • (5) For each "tenant" subfolder a sub-Override Map is created (SOM). The structure of SOM is identical to the one of OM with the only difference that subfolders of a tenant subfolder having a name starting with the letter "D" are used in SOM for creating entries in the "ids" map.

Below an exemplar content of the Overrides folder

Name ID SubType
Overrides 00001 AnsTemplateFolder
- GlobaScript 00002 Content Script
- S144 00003 Content AnsTemplateFolder
- - Document Script 00004 Content Script
- D1234 00005 AnsTemplateFolder
- - S0 00006 AnsTemplateFolder
- - - Folder Script 00007 Content Script
- - D5678 00008 AnsTemplateFolder
- - - Node Script 00009 Content Script

and the resulting OM

[
    "globals":[                     
        00002
    ],
    "type": [               
        "144": [            
            00004          
        ]
    ],
    "tenants": [           
        "1234": [         
            "globals": [ ],
            "type": [       
                "0": [
                    00007
                ]
            },
            "ids": [
                "5678": [
                    00009
                ]
            ]
        ]
    }
]

Overrides

CSSmartView:Columns

It's possible to add/remove columns from/to browsing views using Content Scripts stored in the aforementioned folder. E.g.

example 1

//In the execution context of this script:
// - nodesColumns ( a map that associates nodes' ids with their columns definitions). Tipically contains a single entry
// - nodes: the list of nodes records. Tipically contains a single item.
// - req: the original REST request record
// - envelope: the current REST API call envelope

nodesColumns[3156087]?.add([type:43200, data_type:43200, name:"Add. Information", sort_key:"type", key:"_am_info"])

//Must return the revised nodeColumns
return nodesColumns

It is possible to enhance the information associated with nodes with column information injected via Module Suite E.g.

example 4

def drawStatusBar = { node ->

    def statusList = ['Draft', 'Under Revision', 'Approved', 'Published']
    def numSteps = statusList.size()
    def currStep = new Random().nextInt(statusList.size())
    def currStepName = node.name

    def stepStyle = "height:100%; width:calc(100% / ${numSteps}); float:left; background-color:#F0AD4E; box-sizing:border-box;"

    def stepsHtml = ""

    (currStep + 1).times{
        stepsHtml += """<span style="${stepStyle}"></span>"""
    }


    return """ 
    <div style="text-align:center; font-size:.75em">${currStepName}</div>
    <div style="margin:3px 0; padding:0; height:5px; background-color:#eee;">${stepsHtml}</div>""".toString()
}



retVal =  nodes.collect{
    [
        ("D${it.dataid}" as String):[ //The object returned MUST be made of simple types (no GString allowed)
            commands:["am_group", "am_bwf"],
            columns:[
                // Column defined in CSSmartView:Columns as nodesColumns[3156087]?.add([type:43200, data_type:43200, name:"Add. Information", sort_key:"type", key:"_am_info"])
                // columns of type 43200 can be used to inject HTML
                _am_info:drawStatusBar(it)
            ]
        ]
    ]
}
return retVal

CSSmartView:Actions

It's now possible to add custom actions to a node's menu lazy loaded set of actions . E.g.

example 2

/**
This script receives the following variables in the execution context:

- actions: a map that associates the node id to the list of available actions
E.g.
    "12345": {
        "data": {
            "Classify": {
                "content_type": "application/x-www-form-urlencoded",
                "method": "POST",
                "name": "Add RM Classification",
                "href": "/api/v2/nodes/2891606/rmclassifications",
                "body": "{\"displayPrompt\":false,\"enabled\":false,\"inheritfrom\":false,\"managed\":false}",
                "form_href": ""
            },
            "initiatedocumentworkflow": {
                "content_type": "",
                "method": "",
                "name": "",
                "href": "",
                "body": "initiate_in_smartview",
                "form_href": "",
                "wfList": [

                ]
            },
            "zipanddownload": {
                "content_type": "",
                "method": "POST",
                "name": "Zip and Download",
                "href": "/api/v2/zipanddownload",
                "body": "",
                "form_href": ""
            },
            "RemoveClassification": {
                "content_type": "application/x-www-form-urlencoded",
                "method": "POST",
                "name": "Remove Classificfation",
                "href": "/api/v2/nodes/2891606/rmclassifications",
                "body": "",
                "form_href": ""
            }
        },
        "map": {
            "default_action": "open"
        },
        "order": [
            "initiatedocumentworkflow",
            "Classify",
            "RemoveClassification",
            "zipanddownload"
        ]
    }
}

- req: the current HTTP request
- envelope: the REST API request's envelope

By changing the support variable "actions" you can make visible actions defined by scripts in CSVolume:CSSmartView:Commands

**/

actions[3156106].data["am_release"] = [
    body:"am_release"
]
actions[3156106].order.add("3156106")

CSSmartView:Commands

It's possible to define multiple commands in the same script and group them in the same sub-menu. E.g.

example 2

//Commands scripts can now return a list
return [
    [
        am:[
            exec:[
                mode:"group"// (1) This command will act as our flyout
            ]
        ]
        ,scope: "multiple"
        ,group: "info"
        ,flyout: "am_group" // (2) This command will act as our flyout
        ,baricon: null
        ,icon: null
        ,name: "Try Module Suite"
        ,command_key: "am_group"
        ,signature: "am_group"
    ],
    [
        am:[

            confirmation:[
                required:false,
                title:"",
                message:""
            ],
            panel:[
                width:40,
                cssClass:"",
                slides:[
                    [
                        title:"",
                        script:null
                    ]
                ]
            ],
            key:[
                code: 83
                ,message:""
                ,nogui:false
            ],
            exec:[
                mode:"script"
                ,script: 2644067
                ,params:[

                ]
                ,refresh_on_success:true
                ,on_success_action:""
                ,newtab:false
                ,url:""  
            ]
        ]
        ,baricon: null
        ,icon: null
        ,name: "Content Script"
        ,command_key: "am_content_script"
        ,signature: "am_content_script"
        ,scope: "multiple"
        ,flyout:"am_group"
        ,selfBlockOnly: false
    ]
    ...
]

Content Script scripts executed as commands can return execution information to the caller. E.g.

example 4

//Script code...
//Once done...notify caller
json([message:[type:'success', text:"Get the Module Suite. You won't need anything else.", details:"The Module Suite is a comprehensive framework of highly innovative solutions dedicated to OpenText™ Content Server, and includes all the tools you will need to extend, customize, and enrich your Content Server experience."]])

CSSmartView:MetaPanels

It's possible to define meta data panels to be displayed among any object's properties. E.g.

example 2

//Commands scripts can now return a list
return [
    [
        am:[
            exec:[
                mode:"group"// (1) This command will act as our flyout
            ]
        ]
        ,scope: "multiple"
        ,group: "info"
        ,flyout: "am_group" // (2) This command will act as our flyout
        ,baricon: null
        ,icon: null
        ,name: "Try Module Suite"
        ,command_key: "am_group"
        ,signature: "am_group"
    ],
    [
        am:[

            confirmation:[
                required:false,
                title:"",
                message:""
            ],
            panel:[
                width:40,
                cssClass:"",
                slides:[
                    [
                        title:"",
                        script:null
                    ]
                ]
            ],
            key:[
                code: 83
                ,message:""
                ,nogui:false
            ],
            exec:[
                mode:"script"
                ,script: 2644067
                ,params:[

                ]
                ,refresh_on_success:true
                ,on_success_action:""
                ,newtab:false
                ,url:""  
            ]
        ]
        ,baricon: null
        ,icon: null
        ,name: "Content Script"
        ,command_key: "am_content_script"
        ,signature: "am_content_script"
        ,scope: "multiple"
        ,flyout:"am_group"
        ,selfBlockOnly: false
    ]
    ...
]

Content Script scripts executed as commands can return execution information to the caller. E.g.

example 4

//Script code...
//Once done...notify caller
json([message:[type:'success', text:"Get the Module Suite. You won't need anything else.", details:"The Module Suite is a comprehensive framework of highly innovative solutions dedicated to OpenText™ Content Server, and includes all the tools you will need to extend, customize, and enrich your Content Server experience."]])