MicroStrategy - SDK - Addons

mstr-sdk

https://lw.microstrategy.com/msdz/MSDL/931/docs/mergedProjects/websdk/topics/webarch/Overview_of_Page_Execution.htm
https://lw.microstrategy.com/msdz/MSDL/_CurrentGARelease/docs/projects/WebSDK/default.htm#topics/addons/Deciding_Between_Using_Custom_Add-ons_and_Custom_Events.htm?Highlight=report
https://lw.microstrategy.com/msdz/MSDL/_CurrentGARelease/docs/projects/WebSDK/default.htm#topics/addons/Add-on_creation.htm?Highlight=path

What is an add-on?

Reference the MSDZ (Home > Web SDK > Customizing MicroStrategy Web > Part II: Advanced Customization Topics > Customizing Add-ons> Add-on Creation).

Are we limited to only using add-on for reports?

When we implement an add-on, we have to choice of overriding the preCollectData and postCollectData, but do not let this mislead you to believe that add-ons are only applied to "report" objects. In fact, add-ons are applied at the page level, and we can create and register add-ons for any page.

How can we create an add-on?

  1. Launch the Web Customization Editor.
  2. Select the Application Settings tab
  3. Expand Pages
  4. Right click on the appropriate page and select Create New Add-On
  5. Click Browse… next to the Source folder’s text field to select the src folder and click OK
  6. In the Package field, specify anything you want such as com.companyname.addons
  7. In the Name field, specify the appropriate name for your add-on such as ModifyExcelHeader_AddOn
  8. Check appropriate checkboxes (or all of them)
  9. Click Next
  10. Click Finish. This open up a new tab in Eclipse.
  11. Provide the code for your add-on. See the code example below.
  12. Click on the Save icon to save the code.
  13. Double-click on the name of the page to open it in the editor.
  14. Click on the Add-on Properties tab. On the Add-On Properties tab, the custom add-on class ModifyExcelHeader_AddOn, should be listed under Class Name
  15. Restart your Tomcat

Code example:

import com.microstrategy.web.app.addons.AbstractAppAddOn;
import com.microstrategy.web.app.beans.PageComponent;
import com.microstrategy.web.beans.ReportBean;
import com.microstrategy.web.objects.EnumWebReportExecutionModes;
import com.microstrategy.web.objects.WebProperty;
import com.microstrategy.web.objects.WebReportData;
import com.microstrategy.web.objects.WebReportExcelExportSettings;
import com.microstrategy.web.objects.WebReportExportSettings;
import com.microstrategy.web.objects.WebReportInstance;

public class ModifyExcelHeader_AddOn extends AbstractAppAddOn { 

    public String getAddOnDescription() {
        return "Modify Excel Header when exporting";
    }

    public void preCollectData(PageComponent page) {
        String reportHeader = "Report Header";
        ReportBean rb;

        //Get the report bean from the page component  
        rb = (ReportBean) page.getRoot().searchChild("ReportBean");
        try {
            if (rb != null) {

                //Get the report instance  
                WebReportInstance ri = rb.getReportInstance();
                ri.getResultSettings().getDetailsFormatter().setReportDetailsFormat("");
                WebReportExcelExportSettings settings;

                if (ri != null) {

                    //Execute the report in excel mode and get the report details section  
                    rb.setExecutionMode(EnumWebReportExecutionModes.REPORT_MODE_EXCEL);
                    WebReportData reportData = rb.getReportData();
                    String reportDetails = reportData.getReportDetails();

                    if (reportDetails.contains("Empty")) {} else {
                        settings = (WebReportExcelExportSettings) ri.getExportSettings();

                        if (settings != null && settings.getCustomProperties() != null) {

                            // Set the excel header 
                            WebProperty prop = settings.getCustomProperties().getItemByName(WebReportExportSettings.PROPERTY_CUSTOM_EXCEL_REPORT_HEADER);
                            reportHeader = getReportHeader(reportDetails);
                            if (prop != null) prop.setValue(reportHeader);
                        }
                    }
                }
            }
        } catch (Exception e) {}
    }
    //Modify the header value to the desired format 
    public String getReportHeader(String details) {
        String reportHeader = "Report Header";
        String str[] = details.split(" ");

        for (int i = 0; i < str.length; i++) {
            System.out.println("String Array Value At: " + i + "=" + str);
        }
        reportHeader = "Start Date:" + str + " " + "End Date:" + str;

        return reportHeader;
    }
}

How can we register an add-on on multiple pages?

  1. Launch the Customization Editor
  2. Switch to the Application Settings tab
  3. Drill down to Pages
  4. Double click on the appropriate page
  5. Click on the Add-on Properties tab
  6. Click on the Add button in the top section
  7. Click on the Modify button
  8. Type the name of your add-on (it does not need to be exact). The editor will search and display your add-on.

How can I determine the name of the the page that a particular add-on is registered on?

Open up the pageConfig.xml file and search for the Java class name of your add-on.

How can we dynamically add attributes to a report?

  1. Launch the Web Customization Editor.
  2. Switch to the MicroStrategy perspective
  3. Navigate to "Application Settings -> Pages"
  4. Right click on the 'report(Report Execution)' page and select “Create New Add-on”.
  5. Complete the dialog with the source folder, package name, class name… information.
  6. When the Java Editor opens paste the addon code below.
public void preCollectData(PageComponent page) {

        System.out.println("in addon");

        String reportID="B7D08D1B4DC4225FE9D7ADBAA61943F0"; // blank report
        String attrID="8D679D3711D3E4981000E787EC6DE8A4"; //category

        ReportBean rb = (ReportBean) page.getChildByClass(ReportBean.class);
        WebReportInstance ri;
        try {
         System.out.println("name: " +rb.getDisplayName());
         ri = rb.getReportInstance();

         int  status = ri.pollStatus(); 
         System.out.println("status: "+status);
         if(status!=1){
             return;
         }

         WebReportManipulation wrm = rb.getReportInstance().getReportManipulator();

         WebObjectsFactory objectFactory = WebObjectsFactory.getInstance();
         WebObjectSource objectSource = objectFactory.getObjectSource();

         WebObjectInfo subCatInfo = objectSource.getObject("8D679D4F11D3E4981000E787EC6DE8A4", EnumDSSXMLObjectTypes.DssXmlTypeAttribute);

            WebTemplate template = ri.getTemplate();
            template.add(subCatInfo, EnumDSSXMLAxisName.DssXmlAxisNameRows, 3);
            template.remove(subCatInfo);

             wrm.applyChanges();
             rb.collectData();

        } catch (Exception e) {
         e.printStackTrace();
        } 
    }

How can I check to see if the user has a security filter using an add-on?

public void preCollectData(PageComponent pc) {
    String methodName = "preCollectData";
    Log.logger.logp(Level.INFO,  CheckIfSecurityFiltersExistAddOn.class.getName(), methodName, methodName + " invoked!");
    try {
        WebIServerSession sess = pc.getAppContext().getAppSessionManager().getActiveSession();
        WebUser user = (WebUser) sess.getUserInfo();
        WebUserSecurityFilters filters = user.getSecurityFilters();

        if(filters.size() <=0) {
            throw new Exception("Security filter not found!");
        }
    } catch (Exception e) {
        // Whatever you want to do to handle any exception
    }
}

How can I get the name of the project from inside an add-on?

WebIServerSession sess = pc.getAppContext().getAppSessionManager().getActiveSession();
String projectName = sess.getProjectName();

How can output something to the browser from inside an add-on?

ContainerServices cs = pc.getBeanContext().getContainerServices();
OutputStream os = cs.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.print("<html><head><script type='text/javascript'>window.location.href='/MicroStrategy/plugins/CustomESM/jsp/QuantrosCustomErrorPage.jsp?reason=securityFilterNotFound';</script></head><body></body>");
ps.close();

How can we get various information from inside an add-on?

public void postCollectData(PageComponent page){
    WebUserGroup[] gpArry = null;
    RequestKeys keys = page.getAppContext().getRequestKeys();
    String usr = page.getAppContext().getAppSessionManager().getUserName();
    WebIServerSession sesh = page.getAppContext().getAppSessionManager().getActiveSession();

    // Get Project Name
    String projectName = page.getAppContext().getAppSessionManager().getActiveSession().getProjectName();

    WebObjectSource oSource = page.getAppContext().getAppSessionManager().getActiveSession().getFactory().getObjectSource();

    //Collect the ReportBean from the current page
    ReportBean rb= (ReportBean) page.getRoot().searchChild("rb");

    // Get the cookies from the current page
    ContainerStringCollection cookies = page.getAppContext().getContainerServices().getCookieStrings();

    // Get the user object
    WebIServerSession sess = page.getAppContext().getAppSessionManager().getActiveSession();
    WebUser user = (WebUser) sess.getUserInfo();

    // Get the login ID
    String user = page.getAppContext().getAppSessionManager().getUserName();

    // Get the login ID
    String loginID = sess.getLogin(); // This returns an empty string

    WebUser user = (WebUser) session.getUserInfo();
    user.populate();
    String loginID = user.getLoginName(); // This seems to work

    // User GUID:
    String uid = user.getID();

    // User's full name
    String fullname = user.getDisplayName();
    fullname = user.getName();

    WebObjectInfo wOI;
    try {
        wOI = sesh.getUserInfo(true);
        // 3. Access the groups which the current user is a member of
        gpArry = ((WebUserEntity) wOI).getAncestorGroups();
    } catch (WebObjectsException e) {
        e.printStackTrace();
    }

    //Output array of groups
    for(int i = 0; i<gpArry.length; i++){
        System.out.println("Group"+i +" :"+gpArry.getDisplayName());
    }

    // Get the event
    System.out.println("Event is:" + keys.getValue("evt"));

    // 4. Output custom tag parameter added to URL
    System.out.println("Custom value is:" + keys.getValue("CustomTagName"));

    try {
        //Output ReportID
        System.out.println("Report ID is:" + rb.getObjectName());

        //5. Check the event currently processing
        RequestKeys keys = getRequestKeys(page);
        String evt = keys.getValue("evt");

        //check if evt equals "4001" so the logic will only be called when report is executed and not 
        //when a report manipulation is being processed

        //6. The execution status of the report
        //call collectData to make sure the bean has the most recent information
        rb.collectData();

        int status = rb.getXMLStatus();

        //check for EnumRequestStatus.WebBeanRequestSuccessful to perform an action after the 
        //report has run
        //use EnumRequestStatus.WebBeanRequestWaitingForUserInput for prompt answering 
    } catch (WebBeanException e) {
        e.printStackTrace();
    }

    System.out.println("The status of rb is :" + rb.getXMLStatus());
    for(int i=0; i<cookies.getNameCount(); i++){
        //Output cookies
        System.out.println("Cookies: " +cookies.getName(i));
    }
}

What happens when an add-on is registered?

When an add-on is registered in a particular page, it is instantiated and called as a part of the data collection process of the page. It is called just prior to the collection of data from Intelligence Server by the beans on the page. An instance of a com.microstrategy.web.app.beans.PageComponent class that represents one of the components on the page, such as a bean, is passed to an add-on.

How can we pass parameter from the page configuration file to an add-on?

public void preCollectData(PageComponent pg) {
    PrintStream output=System.out;
    output.println("\t\tParameter value: "+addonParamValue);
}

In the previous code sample, the last line prints the value of a data member addonParamValue. This is a parameter that is passed from the Page Configuration file. To do this, create a property in the add-on having the same name as the parameter that is used in the Page Configuration file. Thus, if you want to have a parameter in the Page Configuration file called AddonParam, then include the following methods in the class:

public void setAddonParam(String value) {
    addonParamValue=value;
}

public String getAddonParam() {
    return addonParamValue;
}

So inside your add-on class, a parameter can be a private variable.

How can we register an add-on without using the Customization Editor?

You can edit the pageConfig.xml file directly:

<page desc="Report Execution"... ...>
... ...
<addons>
    <addon name="com.microstrategy.web.app.addons.ReportSetFlagsAddOn"/>
    <addon name="com.microstrategy.sdk.samples.addons.SimpleAddon">
        <properties>
            <property name="AddonParam" source="const" type="string" value="This string is passed to the addon"/>
        </properties>
    </addon>
</addons>
... ...
</page>

An add-on can be registered on multiple pages, and you can pass multiple parameters from the pageConfig.xml file to the add-on. If necessary, one of this parameter can be name of the page.

How can we determine the type of component that the parameter to preCollectData or postCollectData represent?

The preCollectData or postCollectData method accept one parameter of type PageComponent but this can represent different type of components.

In many cases, the add-on acts on a particular Web Component type such as a specific bean. The PageComponent instance represents a number of different object types in the application. The following code demonstrates a way to identify the type of the component. In addition, the component may have a number of child components and so you can also iterate through the children to find a component of interest.

private static ReportBean getReportBean(WebComponent wc) {
ReportBean result = null;

// if the current Web component is a ReportBean then return it
// otherwise, try to find a report bean amongst the children
if (wc instanceof ReportBean) {
    result = (ReportBean) wc;
} else {
    for (int i = 0; i < wc.getChildCount(); i++) {
        //search recursively through all children
        result = getReportBean(wc.getChild(i));
        if (result != null) {
            break;
        }
    }
    return result;
}

What is the purpose of the preCollectData method?

The preCollectData method is a static method. It accepts the PageComponent as the input before collectData() has been called, and allows you to alter the default workflow before data is collected from Intelligence Server. Generally used to set flags on the bean and set how data is collected.

What is the purpose of the postCollectData method?

The postCollectData method is a static method. It accepts the PageComponent as the input before collectData() has been called, and allows you to alter the default workflow after data is collected from Intelligence Server. Generally used to modify the state of a bean or perform an action based on the status of a bean (ready, processing, error).

What does it means to us that an add-on has the PageComponent as the parameter to preCollectData and postCollectData?

Since the PageComponent is available to the add-on, all the beans on the page can be readily accessed for manipulations. Before writing any bean-specific code, check the type of the bean on which the add-on performs the necessary manipulations.

How does MicroStrategy Web uses add-ons?

MicroStrategy Web itself uses add-ons to perform a number of tasks, such as setting execution and result flags and applying rules for incremental fetch on grids and graphs.

How can an add-on tell MicroStrategy Web not to collect data for a specific bean?

In some rare cases, you may want the add-on class to tell the application not to collect data at all for a specific bean. In this case, you would use the isCollectDataRequired method to specify that the application should not call collectData for a given bean.

How can we obtain the name of the report from inside an add-on?

ReportBean rb = (ReportBean) page.getChildByClass(ReportBean.class);
String reportID = rb.getObjectID();
String reportName = rb.getDisplayName();

if(reportID = "<reportID>"){
//execute TN42054 logic
}

How can we determine the name of the page from inside an add-on?

The postCollectData and preCollectData method are provided a PageComponent object (pc). To obtain the name of the page:

PageInfo pi = pc.getPageInfo();
String pageName = pi.getName();
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License