Extending Infinity Portal Components

You can extend the content of the Infinity Process Platform Portal and add your own application components.

Two basic elements are required to develop Portal Add-ons:

This chapter explains the necessary steps in detail using the example of a Support Case Management Application. You need to adjust file names and the like for your own projects accordingly.

As the concept of Utility JARs also allow for the development of persistence-capable Business Objects and transactional Business Services, we will be showing the implementation accordingly in the last section of this chapter.

Application components developed with the concepts described below can be very easily deployed via Infinity Process Platform On Demand, SunGard's Platform as a Service (PaaS) offering.

Base Technologies

You can use any UI technology to extend Portal views UI components.

The development of Infinity Process Platform Portal Add-ons relies on Spring concepts such as Dependency Injection.

For more information about Spring consult www.springsource.com.

Utility JAR Eclipse Project Setup

Infinity Process Platform Portal Add-ons are deployed as Utility JARs containing

Utility Project File Structure

The easiest way to provide these components is to create a Utility Project in Eclipse and set up a source folder structure like

src/
   com/
      bpm/
         framework/
            support/
   META-INF/
      spring/
         support-perspective-context.xml
         support-perspectiveUI-context.xml
      webapp/
         launchpanels/ 
            supportLaunchPanel.xhtml
         views/
            supportCaseManagementPanel.xhtml

Additionally a file support-perspective.portal-plugin and a directory config/ are created which may contain other resources to be loaded from the classpath such as .properties files for localization, e.g.:

Project structure

The content of this file structure is explained in subsequent sections.

Portal Plugin File

The file support-perspective.portal-plugin contains the following

/META-INF/webapp/

Source Code

The directory com/ contains the source code of JSF backing beans and possibly further business logic.

Spring Configurations

The Spring Configuration e.g. META-INF/spring/support-perspective-context.xml contains the definition of all backing beans and their dependency injection relationships, e.g.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean name="supportPerspectiveMessages" class="com.bpm.framework.support.portal.messages.Messages" scope="session"> </bean> <bean name="supportService" class="com.bpm.framework.support.SupportServiceBean" scope="session"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> ... <bean name="supportSession" class="com.bpm.framework.support.portal.SupportSession" scope="session"> <property name="supportService" ref="supportService"/> </bean> <bean name="supportCaseManagementPanel" class="com.bpm.framework.support.portal.SupportCaseManagementPanel" scope="session"> <property name="supportService" ref="supportService"/> </bean> </beans>

Spring UI Configurations

Settings for the Portal Framework are specified in a XML UI configuration file, e.g. support-perspectiveUI-context.xml which contains the definition of Perspectives, Views and Launch Panels (see below), e.g.the following for the definition of a Support Perspective

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ippui="http://infinity.com/bpm/portal/common/uiDefinition"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://infinity.com/bpm/portal/common/uiDefinition http://infinity.com/bpm/portal/common/uiDefinition.xsd">
<ippui:perspective id="supportPerspective" messageBundles="support-perspective-messages"> <ippui:launchPanel name="supportLaunchPanel" include="/plugins/support-perspective/launchpanels/supportLaunchPanel.xhtml" /> <ippui:launchPanel name="extensions"/> <ippui:view name="supportCaseManagementPanel" include="/plugins/support-perspective/views/supportCaseManagementPanel.xhtml" /> </ippui:perspective> </beans

Note, that - as opposed to META-INF/spring/support-perspective-context.xml - the format of this configuration file is an Infinity proprietary format.

For details of the UI Spring Configuration file format consider the following XSD file.

Web Content

All Web content (JSF markup, CSS files, images) are contained under META-INF/webapp.

The structure

/launchpanels
/views

is used for convenience. You may decide for a different structure in your projects.

Embedding in a Infinity Rapid Application Development (RAD) Project

To use all artefacts implemented in the Utility Project described above, you just need to embed this project in a Infinity Process Platform RAD Project by defining

Dependencies

in the Properties Dialog of the RAD Project.

Portal Framework Development

This section explains how to develop and configure UI components to be injected into the Infinity Process Platform Portal.

Portal Structure

The Infinity Process Platform Portal has the following structure

For more details of the Portal Functionality consult the corresponding chapters in the IPP End User Documentation.

Defining Perspectives

Perspectives are the rubber bands to define additional UI components. A Perspective may define

Views are defined in a perspective but can be used independently of the Perspective. All aspects of a Perspective are defined in the Spring UI Configuration File, e.g.

e.g. support-perspectiveUI-context.xml which contains the definition of Perspectives, Views and Launch Panels (see below), e.g.the following for the definition of a Support Perspective

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ippui="http://infinity.com/bpm/portal/common/uiDefinition"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://infinity.com/bpm/portal/common/uiDefinition http://infinity.com/bpm/portal/common/uiDefinition.xsd">
<ippui:perspective id="supportPerspective" messageBundles="support-messages"> ... </ippui:perspective> </beans>

For details of the UI Spring Configuration file format consider the following XSD file.

Defining Launch Panels

Launch Panels are defined in the Spring UI configuration, e.g. support-perspectiveUI-context.xml, e.g. via

...
	<ippui:launchPanel name="supportLaunchPanel"
include="/plugins/support-perspective/launchpanels/supportLaunchPanel.xhtml" /> ...

and implemented as

<f:view xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions">
<ice:panelGrid columns="1" style="margin-top: 10px;">
<ice:commandLink action="#{supportCasePanel.createSupportCase}" styleClass="action-link">
<ice:outputText value="#{supportMessages['launchPanel.createSupportCaseLink.label']} &amp;raquo;"
escape="false" styleClass="action-link"/>
</ice:commandLink>
<ice:commandLink action="#{ippPortalApp.openView}" styleClass="action-link">
<f:param name="viewId" value="supportCaseManagementPanel" />
<f:param name="msgBean"
value="#{supportMessages}" />
<ice:outputText value="#{supportMessages['launchPanel.supportCaseManagementLink.label']} &amp;raquo;"
escape="false" styleClass="action-link"/>
</ice:commandLink>
</ice:panelGrid>
</f:view>

The result looks like the following

Perspective Events

The following two events are fired for perspectives:

To use perspective events, perform the following steps:

  1. Add a controller property to the perspective defined in the Spring UI Configuration *Ui-context.xml, e.g.:
    <ippui:perspective
    id="myPerspective"
    messageBundles="my-perspective-messages"
    controller="myPerspectiveEventHandler"> ... </ippui:perspective>
  2. Define a controller bean in Spring context. This bean may have the scope as session or globalSession as applicable, e.g.:
    <bean name="myPerspectiveEventHandler" class="com.MyPerspectiveEventHandler"
        scope="session">
    </bean>
  3. Finally, implement a controller class, which will receive the Perspective events. The controller bean needs to implement org.eclipse.stardust.ui.web.common.event.PerspectiveEventHandler.
    public class MyPerspectiveEventHandler implements PerspectiveEventHandler
    {
       private boolean initialized;
    
       /* (non-Javadoc)
        * @see org.eclipse.stardust.ui.web.common.event.PerspectiveEventHandler#handleEvent(org.eclipse.stardust.ui.web.common.event.PerspectiveEvent)
        */
       public void handleEvent(PerspectiveEvent event)
       {
          switch (event.getType())
          {
          case ACTIVATED:
             if (!initialized)
             {
                // **********
                // TODO: Initialize Perspective Here
                // **********
                initialized = true;
             }
             break;
          }
       }
    }

Changing a perspective via messages

You can create messages like in the example below to change perspectives.

{
"type":"ChangePerspective",
"data": {"perspectiveId": "ippBccPerspective"}
}

{
"type":"ChangePerspective",
"data": {"perspectiveId": "WorkflowExecution"}
}

{
"type":"ChangePerspective",
"data": {"perspectiveId": "ippAdminPerspective"}
}

These messages can be added as JavaScript to your custom Web applications and then be posted via parent.postMessage(message, "*");.

Working with Views

A view is an arbitrary ICEfaces Facelet which is intended to be opened in the View Area of the Portal Framework, e.g.

<f:view xmlns:h="http://java.sun.com/jsf/html"
   xmlns:f="http://java.sun.com/jsf/core"
   xmlns:ice="http://www.icesoft.com/icefaces/component"
   xmlns:ui="http://java.sun.com/jsf/facelets"
   xmlns:c="http://java.sun.com/jstl/core"
   xmlns:fn="http://java.sun.com/jsp/jstl/functions">
   <ice:outputDeclaration doctypeRoot="HTML"
   doctypePublic="-//W3C//DTD HTML 4.01 Transitional//EN"
   doctypeSystem="http://www.w3.org/TR/html4/loose.dtd" />
<html>
<head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
   <ice:outputStyle href="/plugins/common/css/ipp-portal.css" />
</head>
<body>
   <ice:form id="supportCaseForm">
      <ice:panelGroup style="margin: 10px;">
         <ice:panelGrid columns="2">
            <ice:outputLabel for="oidText" value="ID:" style="line-height:200%" />
            <ice:outputText id="oidText"
               value="#{supportCasePanel.supportCase.oid}">
            </ice:outputText>
            <ice:outputLabel for="creationDateText" value="Creation Date:"
               style="line-height:200%" />
            <ice:outputText id="creationDateText"
               value="#{supportCasePanel.supportCase.creationDate}">
            </ice:outputText>
            <ice:outputLabel for="customerNameEntry" value="Customer Name:"
               style="line-height:200%" />
            <ice:inputText id="customerNameEntry" size="30" maxlength="30"
               value="#{supportCasePanel.supportCase.customerName}">
            </ice:inputText>
            <ice:outputLabel for="customerIdEntry" value="Customer Id:"
               style="line-height:200%" />
            <ice:inputText id="customerIdEntry" size="20" maxlength="20"
               value="#{supportCasePanel.supportCase.customerId}">
            </ice:inputText>
            <ice:outputLabel for="synopsisEntry" value="Synopsis:"
               style="line-height:200%" />
            <ice:inputText id="synopsisEntry" size="80" maxlength="80"
               value="#{supportCasePanel.supportCase.synopsis}">
            </ice:inputText>
            <ice:outputLabel value="Type:" style="line-height:200%" />
            <ice:selectOneMenu size="1"
               valueChangeListener="#{supportCasePanel.selectionChange}"
               style="overflow: auto; width: 250px; margin: 5px; vertical-align: top;">
               <f:selectItems
                  value="#{supportConfigurationPanel.supportCaseTypes}" />
            </ice:selectOneMenu>
            <ice:outputLabel value="Product Component:" style="line-height:200%" />
            <ice:selectOneMenu size="1"
               valueChangeListener="#{supportCasePanel.selectionChange}"
               style="overflow: auto; width: 250px; margin: 5px; vertical-align: top;">
               <f:selectItems
                  value="#{supportConfigurationPanel.productComponents}" />
            </ice:selectOneMenu>
            <ice:outputLabel value="State:" style="line-height:200%" />
            <ice:selectOneMenu size="1"
               valueChangeListener="#{supportCasePanel.selectionChange}"
               style="overflow: auto; width: 250px; margin: 5px; vertical-align: top;">
               <f:selectItems
                  value="#{supportConfigurationPanel.supportCaseStates}" />
            </ice:selectOneMenu>
            <ice:outputLabel for="descriptionEntry" value="Description:"
               style="line-height:200%" />
            <ice:inputTextarea id="descriptionEntry" cols="80" rows="8"
               style="width:280px;height:80px;overflow: auto;"
               value="#{supportCasePanel.supportCase.description}">
            </ice:inputTextarea>
         </ice:panelGrid>
      </ice:panelGroup>
   </ice:form>
</body>
</html>
</f:view>

Defining Views

Views are defined in the Spring UI Configuration File , e.g. support-perspectiveUI-context.xml, e.g. via

...
<ippui:view name="supportCaseManagementPanel"
   include="/plugins/support-perspective/views/supportCaseManagementPanel.xhtml"/>
...

Views are defined in a perspective but can be used independently of the Perspective.

Opening Views

To explicitly open a View, e.g. from a Perspective Launch Panel use the following markup

<ice:commandLink action="#{ippPortalApp.openView}" styleClass="action-link">
   <f:param name="viewId" value="supportCaseManagementPanel" />
<f:param name="viewParams" value="guid=#{row.oid}"/>
<f:param name="msgBean" value="#{supportPerspectiveMessages}" /> <ice:outputText value="Support Case Administration &amp;raquo;" escape="false" styleClass="action-link"/>
</ice:commandLink>

Hereby

See the screenshot below for an example of two Views displayed in the View Area.

View parameters and view initialization

Arbitrary parameters for View initialization can be defined in viewParams in the format

parameter1=value1?parameter2=value2?...

To evaluate the parameters define a Controller Bean for the View in the Spring UI Configuration e.g.

<ippui:view name="supportCaseView" include="..."
   controller="supportCaseView">

whereby the latter Bean name supportCaseView refers to a Bean defined in the Spring Configuration.

The Controller Bean Needs to implement org.eclipse.stardust.ui.web.common.event.ViewEventHandler. In the corresponding handleEvent(ViewEvent event) method it can initialize itself for the CREATED event, by reading view parameters, e.g.

public void handleEvent(ViewEvent event)
{
	if (ViewEventType.CREATED == event.getType())
	{
		Map<String, Object> viewParams = event.getView().getViewParams();
		long oid = viewParams.get("oid");
		setSupportCase(getSupportService().findSupportCaseByOid());
	}
}

Other view events are

Configuring a perspective to open worklist views pre-filtered by descriptor criteria

If you like to open your perspective with a worklist pre-filtered by descriptors, the open view call has to be passed with filters attributes in the f:param section. An easy way to find the filter JSON to be passed is to manually apply the filters and refer the filters attributes in the POST request in your custom xhtml file. Pass additional key value pair in the viewParams section, for example:

<ice:commandLink action="#{ippPortalApp.openView}" onclick="Event.stop(event);" value="Test Descriptor Filter">
   <f:param name="viewId" value="worklistPanel" />
   <f:param name="viewParams" value='id=allActivities&amp;name=Of the Universe, Master (motu)&amp;url=services/rest/portal/worklist/allAssigned&amp;
      filters={"EmpNumber":{"from":120,"to":125}}' />
</ice:commandLink>

Another option to achieve this is to use Java code as shown below:

<ice:commandLink action="#{activitySearchPanel.filterByDescriptor}" value="Test Descriptor Filter">
   </ice:commandLink>
public void filterByDescriptor()
{
   Map<String, Object> params = CollectionUtils.newTreeMap();
      String name = this.getMessages().getString("allActivities");
      String id = "allActivityInstances";
      params.put("id", id);
      params.put("name", name);
      params.put("url", "services/rest/portal/worklist/allActivable");
      String filterJSON = "{\"Account\" : {\"textSearch\":\"Test*\"}}";
      params.put("filters", filterJSON);
      PPUtils.openWorklistViewHTML5("id=" + id, params);
}

Find below example filters for various types of descriptors:

Type Filter

Number

{"descName": { "from" : <number>,"to":<number>}}

Date

{"descName": { "from" : <long>,"to":<long>}}

Sring/Text

{"descName": {"textSearch":"<string>"}}

Boolean

{"descName": {"equals":"<boolean>"}}

Componentizing the internally used Process and Activity Tables

To componentize internally used Process and Activity tables, you need to:

  1. Instantiate and initialize one or both of the following wrapper classes:
  2. Define the custom bean in Spring context
  3. Use the namespace of the tag library exposing the required XHTMLs:
    http://www.eclipse.org/stardust/ui/web/api
  4. Use the following tags:
    <portal:processTable ... />
    <portal:activityTable ... />

Example usage

Create a custom bean, e.g. org.test.MyUseCaseBean and define it in carnot-spring-context.xml:

   <bean name="myUseCaseBean" class="org.test.MyUseCaseBean"
        scope="portalTab">
   </bean>

Define a custom view, e.g. myUseCaseView.xhtml using the tags for Process table and Activity table:

<f:view xmlns:h="http://java.sun.com/jsf/html"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:ice="http://www.icesoft.com/icefaces/component"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:c="http://java.sun.com/jstl/core"
        xmlns:fn="http://java.sun.com/jsp/jstl/functions"
        xmlns:portal="http://www.eclipse.org/stardust/ui/web/api">
        
   <portal:processTable
      bean="#{myUseCaseBean.processTable}"
      id="MyProcessList" 
      title="My Process Table" />

   <portal:activityTable
      bean="#{myUseCaseBean.activityTable}"
      id="MyActivityList"
      title="My Activity Table" />
...

Define the custom view in the Perspective Context:

   <ippui:view name="myUseCaseView" 
      include="/plugins/<myPlugin>/myusecase/myUseCaseView.xhtml" 
      controller="myUseCaseBean"/>

To test your custom view, define a Launch Panel link to open this view.

<ice:commandLink action="#{ippPortalApp.openView}">
   <f:param name="viewId" value="myUseCaseView" />
   <ice:outputText value="MY USE CASE" styleClass="action-link" escape="false"/>
</ice:commandLink>

Modifying Existing Perspectives

Instead of defining new perspectives, you can modify existing perspectives regarding

e.g. via

<?xml version="1.0" encoding="UTF-8"?>
   <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ippui="http://infinity.com/bpm/portal/common/uiDefinition"
      xsi:schemaLocation="
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
      http://infinity.com/bpm/portal/common/uiDefinition http://infinity.com/bpm/portal/common/uiDefinition.xsd">
         <ippui:perspectiveExtension targetPerspective="ippBccPerspective" id="ippPortalFrameworkPerspective" messageBundles="portal-common-messages">
         <ippui:toolbarExtension>
            <ippui:toolbarSection name="configurationPanelToolbar" include="/plugins/common/views/toolbar/configurationPanelToolbar.xhtml" requiredView="configurationPanel" />
         </ippui:toolbarExtension>
         <ippui:viewsExtension>
            <ippui:view name="configurationTreeView" include="/plugins/common/views/configurationTree.xhtml"/>
            <ippui:view name="configurationPanel" include="/plugins/common/views/configurationPanel.xhtml"/>
         </ippui:viewsExtension>
      </ippui:perspectiveExtension>
   </beans>

or via

PortalUiController uiController = PortalUiController.getInstance();
Map<String, IPerspectiveDefinition> perspectives = uiController.getPerspectives();
for (IPerspectiveDefinition pd : perspectives.values())
{
   // Get list of Views and Launch Panels
   pd.getLaunchPanels());
   pd.getViews());

   // To check whether a View or a Launch Panel is global, call method - isGlobal() on that instance
}

Extensions for multiple perspectives

Note that using multiple values for the attribute targetPerspective, specified either as a comma-separated list or *, e.g. for a global perspective extension, is deprecated. In case you provide an extension for multiple perspectives, the following warning appears:

Multiple values for attribute 'targetPerspective' is now deprecated. But it's used in 'ippViewsCommonPerspective'.

Using Common Concepts and Components

This section describes basic concepts and utilities you may use for your Infinity Process Platform Portal Add-ons.

Using Message Dialogs

You can open simple message dialogs by adding push code to your Backing Bean code, for example:

MessageDialog.addMessage(MessageType.ERROR, 
"Support Case Creation", "Support Case cannot be created. Customer order has expired and was not renewed.");

Note that this code is sufficient in case you create your custom worklist and like to show the message dialog from there. If you want to open the message dialog from a JSF activity panel, additionally you need to add the following server push:

public void complete() 
{
    if (executeFormValidation()) 
    {
            
        MessageDialog.addMessage(MessageType.ERROR,
            "Support Case Creation",
                "Support Case cannot be created. Customer order has expired and was not renewed.");
                  
        PortalApplication.getInstance().renderPortalSession();   
                  
        throw new RuntimeException("Support Case cannot be created. Customer order has expired and was not renewed.");
    }
}

Error Dialog

Valid values for the dialog type are

Selection will change the displayed icon.

Using the Generic Table

The Infinity Portal Framework provides an implementation of a Generic Table which allows

Excel and CSV export.

Table Definition in JSF Markup and Binding

To use a Generic Table, define

<pc:genericDataTable bean="#{supportCaseManagementPanel}" id="supportCaseTable" title="#{supportMessages['supportCaseManagementPanel.supportCaseTable.title']}" 
   table="#{supportCaseManagementPanel.supportCaseTable}" sortSupported="true" selectColumn="Select"
   toolbarUrl="/plugins/support/toolbars/supportCaseTableToolbar.xhtml" />

in your JSF markup.

The value of the table should be a property of a Backing Bean which returns a list of implementations of org.eclipse.stardust.ui.web.common.table.DefaultRowModel, e.g.

public class SupportCaseTableEntry extends DefaultRowModel {
	private SupportCase supportCase;
	private boolean selectedRow;

	/**
    * 
    */
	public SupportCaseTableEntry() {
	}

	/**
	 * @param supportCase
	 * @param id
	 * @param name
	 * @param description
	 */
	public SupportCaseTableEntry(SupportCase supportCase, boolean selectedRow) {
		super();
		this.supportCase = supportCase;
		this.selectedRow = selectedRow;
	}

	public SupportCase getSupportCase() {
		return supportCase;
	}

	public String getId() {
		return "" + getSupportCase().getOid();
	}

	public Date getCreationDate() {
		return getSupportCase().getCreationDate();
	}

	public String getSynopsis() {
		return getSupportCase().getSynopsis();
	}

	public String getSynopsisShortcut() {
		if (getSupportCase().getSynopsis() != null && getSupportCase().getSynopsis().length() >= 30)
		{
			return getSupportCase().getSynopsis().substring(0, 30) + " ...";
		}
		else
		{
			return getSupportCase().getSynopsis();			
		}
	}

	public String getDescription() {
		return getSupportCase().getDescription();
	}

	public String getDescriptionShortcut() {
		if (getSupportCase().getDescription() != null && getSupportCase().getDescription().length() >= 30)
		{
			return getSupportCase().getDescription().substring(0, 30) + " ...";
		}
		else
		{
			return getSupportCase().getDescription();			
		}
	}

	public String getState() {
		return "supportCase.state." + getSupportCase().getState();
	}
	
	public String getType() {
		return "supportCase.type." + getSupportCase().getType();
	}

	public String getProductComponent() {
		return "supportCase.productComponent." + getSupportCase().getComponent();
	}

	public String getCustomerName() {
		return getSupportCase().getCustomerName();
	}

	public boolean isSelectedRow() {
		return selectedRow;
	}

	public void setSelectedRow(boolean selectedRow) {
		this.selectedRow = selectedRow;
	}
}

This table object can be initialized via

List<ColumnPreference> fixedColumns = new ArrayList<ColumnPreference>();
ColumnPreference selectColumn = new ColumnPreference("Select",
	"selectedRow", ColumnDataType.BOOLEAN, "Select", true, false);
selectColumn.setColumnRenderType(ColumnRenderType.READ_WRITE);
selectColumn.setColumnAlignment(ColumnAlignment.CENTER);

fixedColumns.add(selectColumn);

ColumnPreference idColumn = new ColumnPreference(
	"Id",
	"id",
	getSupportMessages()
			.getString(
					"supportCaseManagementPanel.supportCaseTable.id.header"),
	"/plugins/support/views/supportCaseTableColumns.xhtml",
	new TableDataFilterPopup(new TableDataFilterSearch()), true,
	true);
ColumnPreference creationDateColumn = new ColumnPreference(
	"CreationDate",
	"creationDate",
	ColumnDataType.DATE,
	getSupportMessages()
			.getString(
					"supportCaseManagementPanel.supportCaseTable.creationDate.header"),
	new TableDataFilterPopup(new TableDataFilterBetween(
			DataType.DATE)), true, true);
ColumnPreference nameColumn = new ColumnPreference(
	"Synopsis",
	"synopsis",
	getSupportMessages()
			.getString(
					"supportCaseManagementPanel.supportCaseTable.synopsis.header"),
	"/plugins/support/views/supportCaseTableColumns.xhtml",
	new TableDataFilterPopup(new TableDataFilterSearch()), true,
	true);
ColumnPreference customerColumn = new ColumnPreference(
	"Customer",
	"customerName",
	getSupportMessages()
			.getString(
					"supportCaseManagementPanel.supportCaseTable.customer.header"),
	"/plugins/support/views/supportCaseTableColumns.xhtml",
	new TableDataFilterPopup(new TableDataFilterSearch()), true,
	true);
ColumnPreference descrCol = new ColumnPreference(
	"Description",
	"description",
	getSupportMessages()
			.getString(
					"supportCaseManagementPanel.supportCaseTable.description.header"),
	"/plugins/support/views/supportCaseTableColumns.xhtml",
	new TableDataFilterPopup(new TableDataFilterSearch()), true,
	true);
ColumnPreference typeColumn = new ColumnPreference(
	"Type",
	"type",
	getSupportMessages()
			.getString(
					"supportCaseManagementPanel.supportCaseTable.type.header"),
	"/plugins/support/views/supportCaseTableColumns.xhtml", true,
	true);
ColumnPreference stateColumn = new ColumnPreference(
	"State",
	"state",
	getSupportMessages()
			.getString(
					"supportCaseManagementPanel.supportCaseTable.state.header"),
	"/plugins/support/views/supportCaseTableColumns.xhtml", true,
	true);
ColumnPreference componentColumn = new ColumnPreference(
	"ProductComponent",
	"productComponent",
	getSupportMessages()
			.getString(
					"supportCaseManagementPanel.supportCaseTable.productComponent.header"),
	"/plugins/support/views/supportCaseTableColumns.xhtml", true,
	true);

List<ColumnPreference> columns = new ArrayList<ColumnPreference>();

columns.add(idColumn);
columns.add(creationDateColumn);
columns.add(nameColumn);
columns.add(customerColumn);
columns.add(descrCol);
columns.add(stateColumn);
columns.add(typeColumn);
columns.add(componentColumn);

IColumnModel columnModel = new DefaultColumnModel(columns,
	fixedColumns, null, "admin", "realm");
TableColumnSelectorPopup columnSelecPopup = new TableColumnSelectorPopup(
	columnModel);

supportCaseTable = new SortableTable<SupportCaseTableEntry>(null,
	columnSelecPopup, null,
	new SortableTableComparator<SupportCaseTableEntry>("synopsis",
			true));

To set the content of the table you may invoke

List<SupportCaseTableEntry> supportCaseTableEntries = new ArrayList<SupportCaseTableEntry>();
List<SupportCase> supportCases = getSupportService()
		.findAllSupportCases();

for (Iterator<SupportCase> iterator = supportCases.iterator(); iterator
		.hasNext();) {
	supportCaseTableEntries.add(new SupportCaseTableEntry(iterator
			.next(), false));
}

supportCaseTable.setList(supportCaseTableEntries);
supportCaseTable.initialize();

Table-specific Toolbars

/plugins/admin-portal/views/supportCaseTableToolbar.xhtml allows you to add table-specific toolbar entries into the toolbar above the table, e.g

f:view xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions">
<ice:panelGrid columns="1">
<ice:commandLink title="Update"
action="#{supportCaseManagementPanel.update}" partialSubmit="true">
<ice:graphicImage value="/plugins/support/css/images/update.png"
styleClass="toolbar-button" />
</ice:commandLink>
</ice:panelGrid>
</f:view>

This way, you can e.g. add buttons to add items to the table.

Table Column Layout

The layout of every table column can be specified in a separate JSF file, e.g.

f:view xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:pc="http://www.sungard.de/carnot/web/portal-plugin/portalcommon">
<ice:panelGroup>
<ice:panelGroup rendered="#{col.columnName == 'Synopsis'}"
panelTooltip="synopsisToolTip">
<ice:outputText value="#{row.synopsisShortcut}" />
</ice:panelGroup>
<ice:panelGroup rendered="#{col.columnName == 'Description'}"
panelTooltip="descriptionToolTip">
<ice:outputText value="#{row.descriptionShortcut}" />
</ice:panelGroup>
<ice:panelGroup rendered="#{col.columnName == 'Id'}">
<ice:commandLink title="Open Details"
action="#{supportCasePanel.open}" partialSubmit="true">
<ice:panelGroup menuPopup="menuSupportCase">
<ice:outputText value="#{row.supportCase.oid}" />
</ice:panelGroup>
<f:param name="supportCaseOid" value="#{row.supportCase.oid}" />
</ice:commandLink>
</ice:panelGroup>
<ice:menuPopup id="menuSupportCase"
style="z-index:999; background-color: white;">
<ice:menuItem value="Reopen"
action="#{supportSession.reopenSupportCase}">
<f:param name="supportCaseOid" value="#{row.supportCase.oid}" />
</ice:menuItem>
</ice:menuPopup>
<ice:panelGroup rendered="#{col.columnName == 'Customer'}">
<ice:outputLink title="Open Customer Details"
value="http://www.salesforce.com/#{row.supportCase.customerId}">
<ice:outputText value="#{row.supportCase.customerName}" />
</ice:outputLink>
</ice:panelGroup>
<ice:panelTooltip id="synopsisToolTip"
style="width: 200px; text-align: left;" styleClass="notesTooltip">
<f:facet name="header">
</f:facet>
<f:facet name="body">
<ice:panelGroup styleClass="notesTooltipContent">
<ice:outputText value="#{row.synopsis}" />
</ice:panelGroup>
</f:facet>
</ice:panelTooltip>
<ice:panelTooltip id="descriptionToolTip"
style="width: 200px; text-align: left;" styleClass="notesTooltip">
<f:facet name="header">
</f:facet>
<f:facet name="body">
<ice:panelGroup styleClass="notesTooltipContent">
<ice:outputText value="#{row.description}" />
</ice:panelGroup>
</f:facet>
</ice:panelTooltip>
<ice:panelGroup rendered="#{col.columnName == 'State'}">
<ice:outputText value="#{supportMessages[row.state]}" />
</ice:panelGroup>
<ice:panelGroup rendered="#{col.columnName == 'ProductComponent'}">
<ice:outputText value="#{supportMessages[row.productComponent]}" />
</ice:panelGroup>
<ice:panelGroup rendered="#{col.columnName == 'Type'}">
<ice:outputText value="#{supportMessages[row.type]}" />
</ice:panelGroup>
</ice:panelGroup>
</f:view>

Style classes for Generic Data Tables

The following style classes can be used to customize Generic Data Tables:

<styleClass> is the attribute passed to the <genericDataTable> tag. Default is genericTable.

These style classes have an effect on the following parts of a data table:

A way to add custom styles is using custom skins. The usage of custom skins is described in chapter Creating and Using Custom Skins in the End User Handbook.

Centering the IceFaces panelPopup inside an IFRAME

The Icefaces panelPopup opened with autoCenter="true" does not correctly center itself when inside an IFRAME. It is placed at the center of the content instead of being placed at the center of the visible area of the IFRAME.

To place the popup at the center of the visible area of the browser, we provide a custom script and perform the following steps:

  1. Assign a unique ID to the popup.
    <ice:panelPopup id="#{bean.beanId}" ...>
  2. Use attribute
    autoPosition="manual"
    instead of
    autoCenter="true"
  3. After the popup is opened invoke the following JavaScript code with the ID assigned to the pop-up in step 1:
    "InfinityBpm.Core.positionMessageDialog('" + <idAssignedToThePopup> + "');"

The following utility method is provided for this:

org.eclipse.stardust.portal.common.util.PopupUtils#addPopupCenteringScript(String popupId)

It places the popup at the center of the visible area of the browser.

Localization

Resource Bundles according to the Java localization mechanism need to be defined in the classpath of the Uitility JAR Project, e.g. under

config/
    support-messages.properties
    support-messages_de.properties

whereby config is added to the build path. These bundles may contain lables for Pespectives and Tab Headers respectively

perspectives.supportPerspective.label=Support Management
views.supportCaseManagementPanel.label=Support Case Management
views.supportCaseManagementPanel.description=Allows to manage support Cases

To use these resource bundles for Perspectives and Views, these Resource Bundles have to be listed in the Spring UI Configuration, e.g. support-perspectiveUI-context.xml, e.g. via

<ippui:perspective id="supportPerspective" messageBundles="support-perspective-messages">

Creating custom Paginator Data Tables

In case you like to build your custom table independent of any of the tables provided by Infinity, you must implement the IQuery interface.

The following is an example implementation, creating a copy based on the current instance:

public class MyQuery implements IQuery, Serializable
{
   /* (non-Javadoc)
    * @see org.eclipse.stardust.ui.web.common.table.IQuery#getClone()
    */
   public IQuery getClone()
   {
      IQuery clone = null;
      try
      {
         byte[] serializedQuery = Serialization.serializeObject(this);
         clone = (IQuery) Serialization.deserializeObject(serializedQuery);
      }
      catch (IOException e)
      {
         // Log / Handle Error 
      }
      catch (ClassNotFoundException e)
      {
         // Log / Handle Error
      }
      return clone;
   }
}

Connecting to the Infinity Process Platform Functionality

You can easily combine functionality of your application in the Portal Framework with Infinity Process Platform functionality exposed via the Portal Framework.

Open Custom Worklists

If you intend to open a specific worklist for your application, e.g. filtered by

The code

import org.eclipse.stardust.common.CollectionUtils;
import org.eclipse.stardust.ui.web.processportal.common.PPUtils;
... public void openSupportOperatorWorklist()
{
Map<String, Object> params = CollectionUtils.newTreeMap();
PortalApplication.getInstance().openViewById("worklistPanel",
"id=" + "bla", params, null, false); PPUtils.selectWorklist(null); }

This way you can restrict or focus workflow participation to specific use cases.

Starting Processes

To start processes and retrieve the first activity matching to the entitlements of the logged in user

try {
long oid = workflowService.startProcess("SupportCaseCreation",
null, true).getOID(); ActivityInstance activityInstance = workflowService.activateNextActivityInstanceForProcessInstance(oid); Map<String, Object> params = CollectionUtils.newTreeMap(); params.put(ActivityInstance.class.getName(), activityInstance); params.put("oid", Long.toString(activityInstance.getOID())); params.put("activityName", activityInstance.getActivity().getName()); PortalApplication.getInstance().openViewById("activityPanel", "oid=" + activityInstance.getOID(), params, null, true); } catch (Exception e) { ... }

Accessing Documents

To open Document Views from Java

Map<String, Object> params = CollectionUtils.newTreeMap();

params.put("documentOID", documentId);

PortalApplication.getInstance().openViewById("documentView", "documentOID="    + documentId, params, null, true);

and from JSF markup

<ice:commandLink action="#{ippPortalApp.openView}">
   <f:param name="viewId" value="documentView" />
   <f:param name="viewParams" value="documentOID={jcrUuid}2ffa5c75-23a8-4705-902b-ac1479114847"/>
   <ice:outputText value="Document A &amp;raquo;" styleClass="action-link"    escape="false"/>
</ice:commandLink>

Adding Business and Database Tier Functionality

Java Persistence API

The Java Persistence API, sometimes referred to as JPA, is a Java programming language framework managing relational data in applications using Java Platform, Standard Edition and Java Platform, Enterprise Edition. JPA can be used to easily achieve persistence to Business Objects in a Utility JAR deployment. For more details on JPA consult the corresponding J2EE Tutorial.

Business Objects and Persistence

Persistent Object Classes

Business objects can be defined as simple Java classes with annotations for JPA-based persistence.

@Entity(name = "SPTCAS")
public class SupportCase { public static final String SUBMITTED_STATE = "SUBMITTED_STATE"; public static final String ANALYZED_STATE = "ANALYZED_STATE"; public static final String BUG_TYPE = "BUG_TYPE"; public static final String ENHANCEMENT_REQUEST_TYPE = "ENHANCEMENT_REQUEST_TYPE"; public static final String CHANGE_REQUEST_TYPE = "CHANGE_REQUEST_TYPE"; public static final String ENGINE_COMPONENT = "ENGINE_COMPONENT"; public static final String PORTAL_COMPONENT = "PORTAL_COMPONENT"; public static final String MODELER_COMPONENT = "MODELER_COMPONENT"; @Id @GeneratedValue(strategy = GenerationType.AUTO) long oid; @Column(name = "STAT") private String state; @Column(name = "TYP") private String type; @Column(name = "COMP") private String component; @Column(name = "SYNPS") private String synopsis; @Column(name = "DESCR") private String description; @Column(name = "CUSTNAM") private String customerName; @Column(name = "CUSTID") private String customerId; @Column(name = "CRTDAT") @Temporal(TemporalType.TIMESTAMP) private Date creationDate; public SupportCase() { super(); oid = 0; creationDate = new Date(System.currentTimeMillis()); state = SUBMITTED_STATE; } public SupportCase(String synopsis, String description, String customerName, String customerId) { this(); oid = 0; creationDate = new Date(System.currentTimeMillis()); this.synopsis = synopsis; this.description = description; this.customerName = customerName; this.customerId = customerId; } ...

Persistence Configurations

The deployment unit also requires a persistence.xml file with the following content

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
version="1.0">
<persistence-unit name="SupportJPAConfiguration"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <jta-data-source>SupportJtaDs</jta-data-source> <class>com.bpm.framework.support.SupportCase</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> </persistence-unit> </persistence>

Business Services

Access to CRUD and other business operations can be provided with a Spring bean with the interface

public interface SupportService {
   	public List<SupportCase> findAllSupportCases();
	public SupportCase findSupportCaseByOid(long oid);
 	public SupportCase createSupportCase(SupportCase supportCase);
}

and the implementation

public class SupportServiceBean extends JpaDaoSupport implements SupportService {
	
	private SupportServiceBean() {
   		super();
   	}

	public List<SupportCase> findAllSupportCases() {
		List<SupportCase> supportCases = getJpaTemplate().find(
			"SELECT e FROM SPTCAS e");

		return supportCases;
   	}
   public SupportCase findSupportCaseByOid(long oid) {
	   return getJpaTemplate().find(SupportCase.class, oid);
   }
   public SupportCase createSupportCase(SupportCase supportCase) {
	   getJpaTemplate().persist(supportCase);
		return supportCase;
   }
}

Database Connectivity

SupportServiceBean inherits its persistence management capabilities from JpaDaoSupport. The corresponding mm is injected via a runtime dependency

<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="carnotXaAuditTrailDataSource" />
<property name="jpaPropertyMap">
<map>
<entry key="javax.persistence.transactionType" value="JTA" />
<entry key="eclipselink.target-server" value="com.bpm.framework.jpa.SpringJtaTxController" />
</map>
</property>
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
<property name="databasePlatform"
value="org.eclipse.persistence.platform.database.DerbyPlatform" />
</bean>
</property>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="persistenceUnitManager">
<bean class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="dataSources">
<map>
<entry key="SupportJtaDs" value-ref="carnotXaAuditTrailDataSource" />
</map>
</property>
</bean>
</property>
</bean>

Transaction Configurations

The transactional behavior is provided with Aspect-oriented Programming via the following declarations in the support-context.xml Spring configuration.

<tx:advice id="txAdvice" transaction-manager="jtaTxManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
	<aop:pointcut id="supportServiceOperation"
		expression="execution(* com.bpm.framework.support.SupportService.*(..))"    />
	<aop:advisor advice-ref="txAdvice" pointcut-ref="supportServiceOperation"    />
</aop:config>


Invoking Portal Pages without Login

In some cases you might like to invoke Portal pages without a required login. For example, in case you have a project that implements a custom portal-plugin JAR. You like to have some XHTML (*.iface) pages in the plugin to be viewable without requiring a login. These could, for instance, be utility Web forms that are used for data-entry but do not necessarily need a login.

To by-pass a necessary login, Infinity Process Platform provides support for publicly accessible URIs. A filter init parameter to control public URI patterns publicUriPatterns exists, which can be added to your web.xml file. The value is a comma-separated list of patterns having an absolute path. Each pattern can have at most one wildcard and this must be either at the first or last position.

The default value is /plugins/<anyId>/public/, which means any page coming from a /public/ sub-folder of arbitrary plugins. There is special support for specifying plugin-relative patterns, targeting any portal plugin. Such patterns should use a prefix of /plugins/<anyId>.

Example

In the following example, two folders have been added to the ipp-views-common.jar:

with a file openSecret.xhtml.

This xhtml file has the following content:

<f:view>
<html>

<f:view xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:ice="http://www.icesoft.com/icefaces/component"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:c="http://java.sun.com/jstl/core"
	xmlns:fn="http://java.sun.com/jsp/jstl/functions"
	xmlns:pc="http://www.sungard.de/carnot/web/portal-plugin/portalcommon">
	
	<html>
	<head>
	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"></meta>

	<title>Secret</title>
	</head>
	<body>
		This is an Open Secret!
	</body>
	</html>

</f:view>
</html>
</f:view>

The following URL will be allowed without a login:

http://localhost:8080/DemoProject/plugins/views-common/public/openSecret.iface

Whereby the following URL entry is not allowed and will redirect to the login page:

http://localhost:8080/DemoProject/plugins/views-common/login.iface?returnUrl=/DemoProject/plugins/views-common/private/openSecret.iface

Note that in case you already have an active session, navigation to the private URL does work.

By default, the pattern used to identify public URLs is /public/*. This is the same as the following in web.xml, but is optional by default:

<filter>
<filter-name>IppPortalLoginFilter</filter-name>
<filter-class>org.eclipse.stardust.ui.web.viewscommon.filter.LoginFilter</filter-class>
<init-param>
<param-name>publicUriPatterns</param-name>
</init-param>
</filter>

Multiple filters can be specified in a comma-separated list, e.g.:

<param-value>/public/*,/global*</param-value>

Writing a Custom Login Filter

You can write a custom login filter for Single sign-on. To write a custom login filter, perform the following steps:

  1. Bootstrap Infinity Process Platform Portal session
  2. Include the following javascript in the context
    protected void includeCustomJS(FacesContext facesContext)
       {
          try
          {
             if (null != facesContext)
             {
                String jsFile = "/plugins/processportal/integration/iframe/iframe-panel-server-support.js";
          
                if (!asList(JavascriptContext.getIncludedLibs(facesContext)).contains(jsFile))
                {
                   JavascriptContext.includeLib(jsFile, facesContext);
                }
             }
          }
          catch (Exception e)
          {
             // Log Error
          }
       }
    	
  3. Call the above code in filters' doFilter() method
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
             throws IOException, ServletException
       {
            ...
            ...
    
            FacesContext facesContext = org.eclipse.stardust.ui.web.common.util.FacesUtils.getFacesContext(servletContext, request, response);
            includeCustomJS(facesContext);
    
            ...
            ...
       }
    	

Without above JS inclusion, the JSF activities won't work properly.

Creating a Plugin Jar to provide custom Portal Skins

Custom skins can be provided in the Portal configuration via a specific plugin jar. The plugin contains skin stylessheets and images that define one or more skins. The following rules apply to such a plugin:

  1. The plugin should contain a fixed folder named skins, which is available at the plugin's public path.
  2. The base path for skins to be loaded from the plugin should be like the following:
    /<plugin-id>/public/skins
  3. Sub-folders under this folder are to be considered as individual skin definitions and can have individual skin names.
  4. Each skin folder contains .css files that will be considered while applying that particular skin.
  5. All files under /<plugin-id>/public are by default accessible without required authentication.

Handling of conflicting Skin Names

In case conflicts of skin names occur, they are handled in the following way:

Login Page

The only css file included at the login page is login.css or as configured via the property Carnot.Login.Skin.StyleSheet. For details on skinning the login page, refer to section Applying a Customizable Branding to the Login Page of chapter Creating and Using Custom Skins.

Example Plugin Structure

The following shows an example plugin structure to be used for providing the skins design-grey and design-red:

META-INF/
   spring/
      plugin-project-context.xml
      plugin-projectUi-context.xml
   xhtml/
      public/
         skins/
            design-grey/
               images/
               grey.css
            design-red/
               images/
                  banner.jpg
               Red.css
               login.css

Provide this structure as jar file and embed it in your runtime, e.g. in a RAD environment as described in section Embedding in a Infinity Rapid Application Development (RAD) Project. The defined skins are then available in the Portal and can be selected in the Configuration view:

Selecting Skin

Upgrading your customized Components to HTML5 Shell Usage

In case you are using customized views or perspectives created with Infinity versions earlier than 7.2, you need to perform some changes on your custom code: