15 Implement Search Functions in the UI Shell

This chapter discusses how to implement search functions in the UI Shell page template that is used to build web pages.

This chapter includes the following sections:

For more information about the features, see:

15.1 Adding Attachments Support to Global Search

This section assumes that you already have performed the basic attachments setup tasks on your searchable view object, just like you would any other view object.

Oracle Fusion applications has support for attachments using functionality delivered by Oracle Enterprise Crawl and Search Framework (ECSF). This functionality is integrated at crawl time of the Search view object, whereby ECSF will look at the crawled Search view object for attachment child view objects. If there are attachments defined this way, it will use Oracle Fusion Middleware Extensions for Applications Attachments APIs to retrieve the contents of the attachment, and they are added to the crawled document. All attachments found this way are added, and become a blob of data in the indexed document. All references to the file name, file type, and so on, are lost. Refer to the ECSF class oracle.ecsf.fusion.FusionAttachmentImpl for details.

Note that existing ECSF implementation will only crawl attachments attached to the Search view object, not the attachments of child objects. For example, a Purchase Order with attachments will have the attachments crawled, but if the Purchase Order has Purchase Order lines, which also have attachments, these will not be crawled.

Searching attachments includes these features:

  • Keyword Search : Users can perform a keyword search through several different entities within Oracle Fusion applications.

  • Search results drilldown : Users can drill down on a specific record or documents included in the search results UI.

  • Entity keyword search : Users can perform keyword search on a specific entity.

  • Keyword search configuration : Administrators can configure the behavior of the keyword search by specifying what entities and fields will be used during the search process.

  • Attachments Search Security: Attachments inherit security from the parent object to which the attachment is attached. This parent object security should be taken in consideration when defining attachments on searchable view objects and exposing them to the ECSF crawl.

Overview of Attached Documents Search

In a similar way to the existing ECSF crawl functionality, the Applications Core PreIndexProcessor can detect Attached documents at crawl time. It then stores in the indexed document this additional metadata about those attached documents to use when rendering the search result:

  • Document Id

  • Document Name

  • Document Type

At search time, this data is used to show secured links in the existing search result so the user can click them and open the attachment.

Advantages of the Applications Core Attachments search functionality include:

  • Minimal increase in data volumes or crawl time.

  • Attachments are immediately available from the search results screen and can be opened in the same way as they can on the attachments UI.

  • Attachments can be searched by file name and type.

15.1.1 How ECSF Crawl Operates

The ECSF Crawl of attachments does not handle categories or data security. In fact, due to the way it breaks the view links, it breaks the data security built into the view link and attachment code that uses it. No data security clause is added to the attachment query.

Background on how ECSF crawls master/detail relations

When ECSF crawls a Search view object that has detail relations, it does not use the default BC4J view links to generate the object tree of view objects. It breaks the view links and runs the master and detail queries separately, then joins the data using knowledge of the foreign keys in the view link. (This applies also to grandchildren and has nothing to do with attachments specifically; however, because an attachment is implemented as a child object, it is handled this way.)

For example, in crawling a simple Search view object with attachments, the basic algorithm is:

For each batch of Search view object row keys (typically 100)
     Query the Search view object for batch (1),
     Obtain key values of parent end of view link,
     Query child (attachment view object) with IN clause with parent PK values (2),
     For each record, match the parent (1) record with the child (2) based on the key values defined for view link.
End For;

This shows an example of the attachment query for (2). PK1_VALUE and ENTITY_NAME are the attributes used in the view link in this example, and the values 1, 2, 3 and 'GS_PRODUCTS' come from the master Search view object.

SELECT * FROM (SELECT FndAttachedDocumentsEO.ATTACHED_DOCUMENT_ID,
       FndAttachedDocumentsEO.DOCUMENT_ID,
       FndAttachedDocumentsEO.LAST_UPDATE_DATE,
       FndAttachedDocumentsEO.LAST_UPDATED_BY,
       FndAttachedDocumentsEO.ENTITY_NAME,
       FndAttachedDocumentsEO.PK1_VALUE,
       FndAttachedDocumentsEO.PK2_VALUE,
       FndAttachedDocumentsEO.PK3_VALUE,
       FndAttachedDocumentsEO.PK4_VALUE,
       FndAttachedDocumentsEO.PK5_VALUE,
       FndDocumentsEO.DOCUMENT_ID AS DOCUMENT_ID1,
       FndDocumentsEO.DATATYPE_CODE,
       FndDocumentsEO.DESCRIPTION,
       FndDocumentsEO.FILE_NAME,
       FndDocumentsEO.USAGE_TYPE,
       FndDocumentsEO.DM_NODE,
       FndDocumentsEO.DM_FOLDER_PATH,
       FndDocumentsEO.DM_TYPE,
       FndDocumentsEO.DM_DOCUMENT_ID,
       FndDocumentsEO.DM_VERSION_NUMBER,
       FndDocumentsEO.URL,
       FndDocumentsEO.TITLE,
       decode(datatype_code, 'SHORT_TEXT', nvl(FndDocumentsEO.title, 'Undefined'), 'FILE', nvl(FndDocumentsEO.title, FndDocumentsEO.file_name), 'WEB_PAGE', nvl(FndDocumentsEO.title, FndDocumentsEO.url)) attachment,
       FndAttachedDocumentsEO.ENTITY_ATTRIBUTES,
       FndDocumentsEO.DM_REPOSITORY,
       FndDocumentsEO.DOCUMENT_ATTRIBUTES,
       FndDocumentEntitiesEO.DOCUMENT_ENTITY_ID,
       FndAttachedDocumentsEO.CATEGORY_NAME,
       FndDocumentsEO.STATUS,
       FndDocumentsEO.DOWNLOAD_STATUS,
       FndDocumentCategoriesEO.CATEGORY_ID,
       FndDocumentCategoriesEO.USER_NAME,
       FndDocumentCategoriesEO.MODULE_ID,
       FndDocumentsEO.URI,
       FndDocumentsEO.ENT_APP_SHORT_NAME,
       FndAttachedDocumentsEO.SEQ_NUM,
       FndAttachedDocumentsEO.PRIMARY_CATEGORY_FLAG
FROM FND_ATTACHED_DOCUMENTS FndAttachedDocumentsEO, FND_DOCUMENTS_VL FndDocumentsEO, FND_DOCUMENT_ENTITIES_VL FndDocumentEntitiesEO, FND_DOCUMENT_CATEGORIES_VL FndDocumentCategoriesEO
where FndAttachedDocumentsEO.document_id = FndDocumentsEO.document_id
and FndDocumentEntitiesEO.entity_name = FndAttachedDocumentsEO.entity_name
and FndAttachedDocumentsEO.category_name = FndDocumentCategoriesEO.category_name) QRSLT WHERE ((PK1_VALUE,ENTITY_NAME) IN ((:1,:2),(:3,:4),(:5,:6),(:7,:8),(:9,:10),(:11,:12),(:13,:14),(:15,:16),(:17,:18),(:19,:20),(:21,:22),(:23,:24),(:25,:26),(:27,:28),(:29,:30),(:31,:32),(:33,:34),(:35,:36),(:37,:38),(:39,:40),(:41,:42),(:43,:44),(:45,:46),(:47,:48),(:49,:50),(:51,:52),(:53,:54),(:55,:56),(:57,:58),(:59,:60),(:61,:62),(:63,:64),(:65,:66),(:67,:68),(:69,:70),(:71,:72),(:73,:74),(:75,:76),(:77,:78),(:79,:80),(:81,:82),(:83,:84),(:85,:86),(:87,:88),(:89,:90),(:91,:92),(:93,:94),(:95,:96),(:97,:98),(:99,:100))) ORDER BY SEQ_NUM DESC NULLS LAST, LAST_UPDATE_DATE DESC>
<oracle.adf.model> <ViewObjectImpl> <bindParametersForCollection> <[95222] Bind params for ViewObject: [oracle.apps.fnd.applcore.attachments.uiModel.view.AttachmentsVO]SearchRootApplCoreAM.AttachmentsVO_threadId92_1>
<oracle.adf.model> <OracleSQLBuilderImpl> <bindParamValue> <[95223] Binding param "1": 1>
<oracle.adf.model> <OracleSQLBuilderImpl> <bindParamValue> <[95224] Binding param "2": GS_PRODUCTS>
<oracle.adf.model> <OracleSQLBuilderImpl> <bindParamValue> <[95225] Binding param "3": 2>
<oracle.adf.model> <OracleSQLBuilderImpl> <bindParamValue> <[95226] Binding param "4": GS_PRODUCTS>
<oracle.adf.model> <OracleSQLBuilderImpl> <bindParamValue> <[95227] Binding param "5": 3>
<oracle.adf.model> <OracleSQLBuilderImpl> <bindParamValue> <[95228] Binding param "6": GS_PRODUCTS>
<oracle.adf.model> <OracleSQLBuilderImpl> <bindParamValue> <[95229] Binding param "7": 3>
<oracle.adf.model> <OracleSQLBuilderImpl> <bindParamValue> <[95230] Binding param "8": GS_PRODUCTS>
<oracle.adf.model> <OracleSQLBuilderImpl> <bindParamValue> <[95231] Binding param "9": 3>
<oracle.adf.model> <OracleSQLBuilderImpl> <bindParamValue> <[95232] Binding param "10": GS_PRODUCTS>
....

What this means is that all attachments directly attached to the Search view object are crawled, regardless of their category or the existence of security.

As the attachment is effectively inlined in the searchable document (this is how any attachment in SES currently works), a user with access to the Search view object can search on content from a secured attachment, even if the user has no data security rights on the attachment.

There is a fundamental mismatch in the security of the Search view object and secured attachments due to the "public user" nature of the crawl. The security of the attachment is effectively promoted to the functional security of the Search view object.

These secure attachments should not be crawled if they cannot be independently secured.

Due to this breaking of the View Link, if more than one child attachment is present, the attachments will be doubled up. This is because the View Link does not capture the attachment category (it is a custom attribute on the View Link).

15.1.2 How the Global Search Results UI Shows Existing Attachments

This section explains the design and behavior of how the results UI shows attachments.

The design:

  • Shows attachments in the results UI using the same descriptions, tooltips, and so on, that the attachments component does.

  • Allows the user to click an attachment in the results UI and have the attachment open.

  • Shows keyword bolding in the attachment name (if any).

  • Allows developers to promote grandchildren attachments if they want. So, for example, Purchase Order line attachments are shown with the Purchase Order result.

  • Works across attachment categories.

  • Suppresses secured attachments.

  • Picks up Search view objects with changes attachments and flag for recrawling (but not grandchildren attachments).

  • Works with the existing tag crawl functionality.

15.1.3 How Crawling Is Done

Crawling is done using the FndSearchPlugin that can be referenced by developers, either directly or as a base class to existing plugins.

The TagSearchPluginis deprecated and stubbed with the removal of Webcenter tags. Developers should switch to FndSearchPlugin.

Figure 15-1 Configuring the Search Plugin

Configuring the Search Plugin

There are two arguments to determine how attachments are crawled:

  • ATTACHMENT_VIEW_LINK_ACCESSOR_PATHS - A comma-separated list of accessor paths to Attachment view objects.

  • ATTACHMENT_INCREMENTAL_CRAWL_ENTITY_NAME - The name of the attachment entity used in incremental crawls to reverse lookup changed attachments.

Attribute ATTACHMENT_VIEW_LINK_ACCESSOR_PATHS

This required attribute defines the attachments that will be considered as part of the crawl. Without it, the default attachment behavior of ECSF will be unchanged. That is, all direct attachments of the Search view object will be crawled, but nothing will be shown in the Search UI.

It is possible to have multiple attachment view links from a master view object, as well as attachments stored against detail view objects in a master-detail relation.

An accessor path is a dot-separated set of accessors to an Attachment view object. For example, GsProductAttachmentsAccessor is the view link accessor to the AttachmentVO directly under the Search view object.

GsProductDetails.GsProductDetailsAttachmentsAccessor is for an attachment under a detail-level view object.

Any number of these paths can be specified, although it is expected to be small (likely only 1).

As an example, accessors in the Master view object for this would be:

<ViewLinkAccessor
  Name="GsProductAttachmentsAccessor"
  ViewLink="oracle.apps.model.GsProductAVL"
  Type="oracle.jbo.RowIterator"
  IsUpdateable="false"/>
<ViewLinkAccessor
  Name="GsProductDetails"
  ViewLink="oracle.apps.model.GsProductToDetailsVL"
  Type="oracle.jbo.RowIterator"
  IsUpdateable="false"/>

Accessors in the Detail view object for this would be:

<ViewLinkAccessor
  Name="GsProductDetailsAttachmentsAccessor"
  ViewLink="oracle.apps.model.GsProductDetailsAVL"
  Type="oracle.jbo.RowIterator"
  IsUpdateable="false"/>

When creating master-detail relations like this, do not include the accessor in the detail view object. If you do, ECSF will try to follow the links from Master to Detail to Master to Detail until it reaches the maximum depth of 7 levels.

In this case, you will see a crawl error similar to:

<VODocumentImpl> <assign> Deep-level view objects not supported. view object is: oracle.apps.model.GsProductDetailsVO Level: 7

Attribute ATTACHMENT_INCREMENTAL_CRAWL_ENTITY_NAME

This attribute defines the attachment entity name that will be used when determining which Search view object records need to be flagged for re-crawl because they have had changes to their attachments. Because adding an attachment does not change the Last_update_date of the parent view object row, there needs to be a reverse lookup mechanism to force these additional rows onto the incremental crawl queue.

Note that only newly-added attachments and updated attachments (including the title) will be found for recrawl. There is no mechanism to detect deleted attachments.

This attribute is not required. If it is not supplied, attachment additions to a view object row that is otherwise unchanged (and hence on last_update_date change) will go un-noticed.

Internal Stored Attribute FND_ATTACHMENT_METADATA

Attachments found during the crawl will be recorded in the Search view object in the predefined stored string attribute FND_ATTACHMENT_METADATA. This attribute needs to be defined by the developer in the Search view object definition and flagged as a stored attribute. It should have no data in it; it will be over-written by the Applications Core preIndexProcessor at crawl time. An example of defining an attribute is shown here using a transient attribute.

Figure 15-2 Defining an Attribute

Defining an Attribute

What is stored in this attribute is an XML structure for all the attachments stored against a searchable object. As an example, here are four attachments stored against a row:

<Attachments>
  <Attachment documentId="300100024947778" datatypeCode="FILE" title="Latte.doc" fileName="Latte.doc" />
  <Attachment documentId="300100024947776" datatypeCode="FILE" title="UIShell_dtpreview.png" fileName="UIShell_dtpreview.png" />
  <Attachment documentId="300100024881786" datatypeCode="WEB_PAGE" title="oracle" description="Oracle homepage" fileName="http://oracle.com" />
  <Attachment documentId="300100024881784" datatypeCode="TEXT" title="a quick note" description="a quick note" fileName="a quick note" />
</Attachments>

Having these attributes, which are defined in this table, stored in the Search view object means they are searchable by default, and the UI can use the values to quickly decode the values without costly database look-ups when rendering the result.

Table 15-1 Attributes Definitions

datatype Code documentId Title Description File Name

FILE

documentId

nvl(title, fileName)

description

fileName

WEB_PAGE

documentId

nvl(title, URL)

description

URL

TEXT

documentId

nvl(title, fileName)

description

fileName

TMURL

Not supported and there will be no attributes stored for it in SES.

nvl(title, URI)

description

URI (not URL)

FOLDER

Not supported and there will be no attributes stored for it in SES.

     

15.1.3.1 Mapping AttachmentVO Attributes into Search Metadata

Note that the XML is supplied without a schema; it is an internal part of applcore search and may change at any time without notice.

Attachments found at detail levels are pulled up to the parent level. If this is not required, do not include an accessor path to this level so these attachments will not be crawled.

Attachments in all categories are included. However, if that document entity has security enabled (no matter what the data security rules are), the attachments assigned to that document entity are removed from the indexed document and therefore not crawled. Their details will not be in the example XML.

The following types of attachments will be available as links:

  • FILE

    An attached file. This is the primary use case.

  • WEB_PAGE

    A URL, such as http://www.companyname.com. The link will be placed verbatim in the results UI as an af:goLink. Note that ECSF does not crawl Web page attachments.

  • TEXT

    Text typed by the user. This is implemented by attachments internally as a hidden file, so is practically the same as FILE.

The following types of attachments will not be available as links:

  • FOLDER

    A reference to a folder in Oracle WebCenter Content. Requires use of a WebCenter Content folder taskflow.

  • TMURI

    Topology Manager URI. This is a URI offset to a web application defined in topology. This is a minor use case used in migration and is not supported by search.

15.1.4 How Attachments Are Shown

Attachments will be shown on a line below the Other Actions in the Search Results. That is the lowest part of a result item.

The text of the link will be the Attachment title, which generally defaults to the file name in the Attachments UI, but may be over-ridden by the user when the attachment is uploaded.

The tool tip of the attachment will be the attachment description entered by the user. If this is not present, the title value will be used.

Attachments will be sorted and displayed in the results alphabetically.

All information is available in the stored attribute of the Search view object to render attachment links in the UI with no additional lookup work, and therefore no significant performance overhead.

Figure 15-3 Attachments Displayed in the Search Results

Attachments Displayed in the Search Results

15.1.5 How Attachments Are Opened

Clicking an attachment will open the content for the user. The browser settings will determine how this is done and should be in line with what the Attachment component does.

Figure 15-4 Opening an Attachment

Opening an Attachment

For example, clicking a JPG attachment normally will open it directly in a browser tab, while clicking a .doc file will likely open it in Microsoft Word; this depends on the individual web browser configuration.

The different attachment types have different requirements to be able to download the attachment; some can be directly clicked; others need intermediate lookups done in the middle tier.

In addition, there is a security implication for attachments stored in WebCenter Content. The "real" url to the WebCenter Content attachment is long and needs to be determined within a few minutes of being used, because it has an implicit timeout in it. This means it cannot be pre-determined and stored in the Oracle Secure Enterprise Search Indexed Document. Similarly, on rendering of the results, it is not possible to look up the link in an efficient manner and show the user an af:goLink to the Attachment for all attachments in the search result. (The user may walk away from the screen and come back and click the link.)

The link that the user clicks needs to do the work of determining the "real link" at the point of click.

15.1.6 How to Implement Attachments Search

There are two cases to consider:

  • If the Search view object has no plugin currently configured, simply use the Applications Core plugin.

  • If the Search view object has a plugin already, set up the existing plugin to inherit the Applications Core plugin.

15.1.6.1 Using the Applications Core Plugin

In the ECSF Search view object design time, add a Search Plugin to the Applications Core class oracle.apps.fnd.applcore.search.plugin.FndSearchPlugin.

Configure attachments that will be crawled

Add a parameter with a comma-separated list of accessor paths with the key ATTACHMENT_VIEW_LINK_ACCESSOR_PATHS.

Configure Search view object incremental crawl detection of Search view objects with new attachments

Optionally, add an attribute with the key INCREMENTAL_CRAWL_ATTACHMENT_ENTITY_NAME and a value of the Attachment Entity Name (the value you enter in the Attachment View Link wizard, such as GS_PRODUCTS).

This will add Search view objects to the crawl list when new attachments are added. This is useful because the Search view object last_update_date value is not updated when an attachment is added, and the document will not be detected as needing a re-crawl. This process will flag the individual Search view object row to be recrawled. Note that it is not possible to detect changed attachments this way at the detail level (for example a purchase order line's attachments), because the reverse lookup would obtain the primary key of the line item and not the Search view object.

15.1.6.2 Enhancing an Existing Plugin

Developers that have an existing plugin should extend the Applications Core plugin and call super() on the methods that implement attachment functionality. Use the same parameters to the plugin as already described.

public class AppsSearchPlugin
  extends FndSearchPlugin
  implements PreIndexProcessor,
             ChangeListener
{
  /**
   * Hook into the pre index process of the crawl so we can annotate the Search view object
   * with apps specific information such as tags and attachment metadata.
   * @param ctx Search Context.
   * @param docs Documents to iterate and find tag children for.
   */
  public void preIndexProcess(SearchContext ctx,
                              List<IndexableDocument> docs)
{
  super.preIndexProcess(ctx, docs);
  // custom apps functionality ...
}
    /**
   * Get the change list for crawling.
   * @param ctx Search Context.
   * @param changeType the type of the change requested. Must be one of following
   * <ul>
   * <li>ChangeListener.UPDATE<li>
   * <li>ChangeListener.INSERT<li>
   * <li>ChangeListener.DELETE<li>
   * </ul>
   * @return an iterator of a list of primaryKeys. Returns an empty iterator if
   * nothing changed.
   */
  public Iterator getChangeList(SearchContext ctx, String changeType)
  {
     Iterator ret = super.getChangeList(ctx, changeType);
    // custom apps functionality ...
    return ret;
  }
}

15.1.6.3 Adding the Transient Stored Attribute FND_ATTACHMENT_METADATA

Add a transient string attribute to the Search view object named FND_ATTACHMENT_METADATA to store Applications Core attribute metadata. Flag this attribute as stored in the Search view object definition.

15.2 Implementing Recent Items

Recent Items tracks a list of the last 20 task flows visited by a user. The Recent Items list is persistent across user sessions and a task can be restarted from the Recent Items list. The feature is automatically turned on and will be available automatically in pages using the UI Shell template. Security must be disabled to turn Recent items off.

Before You Begin

For the Recent Items feature to work, you must configure the user session and ADF Security. See Implementing Application User Sessions. Without security enabled, recent items will not be captured because the data is recorded for each authenticated user.

15.2.1 How to Choose Labels for Task Flows

Recent Items records the task flow labels for a task flow that has started. Therefore, you must carefully choose the labels for task flows, and must provide task flow labels for all task flows, even if they are meant to be used in no-tab mode (see Supporting No-Tab Work Areas ).

15.2.2 How to Call Sub Flows

The openMainTask method is used to open a new task in the main area of Oracle Fusion web applications that use UI Shell templates. Besides opening a new tab, openMainTask also pushes a new task flow history object onto a stack, which is used to keep track of all task flows that have been opened. The task flow ID and its associated parameter values are encapsulated in the task flow history object.

Having this information, the call to closeMainTask pops the stack to get the last task flow ID and its parameter values that were displayed, and reinitializes the main area with that task flow and parameter information.

When a task flow is called from the local area task flow using task flow call activity, it is called a sub flow. By default, sub flows will not be recorded on the stack as described. Two new APIs are exposed in FndUIShellController data control for registering sub flows: openSubTask and closeSubTask.

15.2.2.1 Sub Flow Registration APIs

Use the openSubFlow and closeSubFlow APIs to record sub flows to Recent Items. Whenever an ADF Controller task flow call takes place, no notification is raised to Applications Core or UI Shell. So, unlike starting a task from a tasks list, you need to explicitly notify the UI Shell for sub flow calls.

When the openSubTask API is called before a subflow is started, the sub flow ID and its parameter values are pushed onto the stack. Applications Core also notifies the Recent Items implementation with recorded task flow information. This essentially makes a sub flow able to be bookmarked by Recent Items, and can be started directly from the selection of menu items on Recent Items.

Note that registering sub flows to Recent Items is optional.

Implementation

This API is exposed as the data control methods FndUIShellController.openSubTask and FndUIShellController.closeSubTask that can be dragged and dropped to page fragments to create links to notify the UI Shell. The FndUIShellController data control is automatically available to all Oracle Fusion applications that reference Applications Core libraries.

Example 15-1 shows the signature and Javadoc of the method.

All the parameters required to be passed in openSubTask are exactly same as used by the Navigate API. For more information, see Introducing the Navigate API.

Example 15-1 Recent Items API

/**
* Notify UIS hell to record a given sub flow on the stack and 
* also notify Recent Items to include it on the list.
*
* @param taskFlowId Task flow to open
* @param parametersList Parameters list for the task flow.
*                       This is a semicolon delimited String
*                       of name-value pairs. For example,
*                       "param1=value1;param2=value2".
*
* @param label Label for the task flow.
* @param keyList Key list to locate the task flow instance.
*                This is a semicolon delimited keys or key-value pairs.
*                For example, "key1;key2=value2". If only the key is specified,
*                the value is picked up from parametersList with the same
*                name as the key.
*
* @param taskParametersList Parameters list to pass in to the task flow to open
*                in the target workspace. This is a semicolon delimited String
*                of name value pairs. For example,
*                "param1=value1;param2=value2."
*
* @param methodParameters This can be used for passing
*                Java object into the task flow that is specified 
*                in taskFlowId parameter. Use <code>setCustomObject() API</code>
*                in FndMethodParameters for setting the java object.
*
* @return For internal Contextual Event processing
*/
 
public FndMethodParameters openSubTask(String taskFlowId,
String parametersList,
String label,
String keyList,
String taskParametersList,
String viewId,
String webApp,
FndMethodParameters methodParameters)
 
/**
* Closes the currently-focused sub-task and the focus moves to the 
* task from which this sub-task was started.
*
* @param methodParameters For future implementation. No-op for now.
* @return For internal Contextual Event processing
*/
public FndMethodParameters closeSubTask(FndMethodParameters methodParameters)

15.2.2.2 openSubTask API Labels

The openSubTask API accepts the same set of parameters as used by the Navigate API. If no label is specified in the openSubTask API, Recent Items will register it with the name of the parent task flow's label. You should set a different label based on the business use case in the openSubTask API. Failing to do so will register this flow with the same label as the parent task flow's label and, therefore, will make it impossible to distinguish between the parent flow and the sub flow entry in the Recent Items list.

15.2.2.3 Starting from Recent Items

Whatever task flow details are registered while invoking the openSubTask API will be used by Recent Items to start it. Recent Items takes care of starting the task flow in the right work area and web application. You do not need to do anything for that. Because the openSubTask API supports parametersList, you can pass some requirement-specific values to it while registering the task flow to Recent Items. On starting, those passed values are available in the pageFlowScope. You can analyze these values and make decisions, such as if you need to first initialize the parent flow, or if you need to set Visible to False on some of the actions on the page.

15.2.3 How to Enable a Sub Flow to Be Bookmarked in Recent Items

To record sub flows into the Recent Items list, applications need to call the openSubTask API right before sub flows are started. openSubTask takes parameters similar to the Navigate API. One of these is task flow ID. For this, you need to specify the parent flow's ID (or main task's ID). In other words, sub flows need to be executed by using the parent flow, even though they are started from the Recent Items menu.

If your sub flow does not need to be bookmarked by Recent Items, you do not need to change anything. Otherwise, you need to modify your parent flow and sub flow as described in this section. After the changes, sub flows can be started in two ways:

  • From original flows

  • From Recent Items menu items using recorded information

Both will start the execution in the parent flow. Because the sub flow must be piggybacked on the parent flow when it is started from the Recent Items menu, you need to change the parent flow following these directions:

  • Add a new router activity at the beginning of the parent flow. Based on a test condition, it will route the control to either the original parent flow or the task flow call activity (that is, the sub flow).

  • Add an optional method call activity to initialize the sub flow before it is started from the Recent Items menu. Product teams can code the method in such a way that it can navigate to the sub flow after initializing the parent state. This allows product teams to render the contextual area, navigating back to the parent flow from the sub flow, and any other customizations.

  • Bind openSubTask to the command component (such as a link or button) which causes the flow to navigate to the task flow call activity in the original parent flow. The openSubTask API registers the parent flow details and input parameters to the sub flow (to be started as a sub flow later) to the Applications Core task flow history stack.

Usually, you do not need to modify your sub flow for this task. However, you can consolidate the initialization steps from two execution paths in this way:

  • Remove initialization parts from both paths in the parent flow. Instead, set input parameters in both paths only.

  • Modify the sub flow to take input parameters.

  • Add a new method call (such as initsubflow) at the beginning of the sub flow to initialize states in the parent flow (for example, parent table) so that the sub flow can be started in the appropriate context.

Note that the design pattern also requires the application to be able to navigate back to the parent flow from the sub flow. The initialization code should take this into consideration, such as by setting up states to allow the sub flow to navigate back.

In this example, you will use an Employee sample implementation to demonstrate the details of this design pattern.

Sub Flow Sample Application

As shown in Figure 15-5, users select Subflow Design Pattern from the task list. They then specify some criteria to search for a specific employee or employees. From the list, they can choose the employee for whom they want to show the details.

Figure 15-5 Example List of Employees

Described in the surrounding text.

The Ename column in the search result table is a link that can be used to navigate to the employee detail page of a specific employee. When this link is clicked, a sub flow (or nested bounded task flow) is called to display the Employee Complete Detail page, as shown in Figure 15-6.

Figure 15-6 Example Employee Complete Detail Page

Described in the surrounding text.

15.2.3.1 Implementing the Sub Flow Design Pattern

The parent task flow named ToParentSFFlow is shown in Figure 15-7.

Figure 15-7 Example Parent Task Flow

Described in the surrounding text.

The router activity decideFlow decides whether the control task flow should go to the original parent task flow path (initParent) or to the sub task flow path (toChild). The condition is defined as:

<router id="decideFlow">
  <case>
    <expression>#{pageFlowScope.Empno == null}</expression>
    <outcome id="__9">initParent</outcome>
  </case>
 
  <case>
    <expression>#{pageFlowScope.Empno != null}</expression>
    <outcome id="__10">toChild</outcome>
  </case>
 
  <default-outcome>initParent</default-outcome>
</router>

The test checks whether the Empno variable in the parent task flow's pageFlowScope is null. #{pageFlowScope.Empno} is set using its input parameter Empno when the parent task flow is called. The input parameters on the parent task flow (that is, ToParentSFFlow) are defined as:

<input-parameter-definition>
  <name>Empno</name>
  <value>#{pageFlowScope.Empno}</value>
  <class>java.lang.String</class>
</input-parameter-definition>

When the parent task flow is started from the task list, the Empno parameter is not set (that is, it is not defined in the application menu's itemNode). Therefore, the parameter is null and the router will route it to the initParent path.

When the sub task flow is recorded through the openSubTask API, the Empno parameter is set on parametersList as:

<methodAction id="openSubTask" RequiresUpdateModel="true"
                 Action="invokeMethod" MethodName="openSubTask"
                 IsViewObjectMethod="false" DataControl="FndUIShellController"
                 InstanceName="FndUIShellController.dataProvider"
                 ReturnName="FndUIShellController.methodResults.openSubTask_FndUIShellController_dataProvider_openSubTask_result">
     <NamedData NDName="taskFlowId" NDType="java.lang.String"
         NDValue="/WEB-INF/oracle/apps/xteam/demo/ui/flow/ToParentSFContainerFlow.xml#ToParentSFContainerFlow"/>
     <NamedData NDName="parametersList" NDType="java.lang.String"
                NDValue="Empno=#{row.Empno}"/>
     <NamedData NDName="label" NDType="java.lang.String"
                NDValue="#{row.Ename} complete details"/>
     <NamedData NDName="keyList" NDType="java.lang.String"/>
     <NamedData NDName="taskParametersList" NDType="java.lang.String"/>
     <NamedData NDName="viewId" NDType="java.lang.String"
                NDValue="/DemoWorkArea"/>
     <NamedData NDName="webApp" NDType="java.lang.String"
                NDValue="DemoAppSource"/>
     <NamedData NDName="methodParameters"
         NDType="oracle.apps.fnd.applcore.patterns.uishell.ui.bean.FndMethodParameters"/>
</methodAction>

You also set up:

  • taskFlowId to be the parent task flow's, not the sub task flow's

  • label to be the sub task flow's

When users click the link (the Ename) to which the openSubTask method is bound, openSubTask will be called. This link component is defined as:

<af:column sortProperty="Ename" sortable="false"
           headerText="#{bindings.ComplexSFEmpVO.hints.Ename.label}"
           id="resId1c2">
  <af:commandLink id="ot3" text="#{row.Ename}"
                  actionListener="#{bindings.openSubTask.execute}"
                  disabled="#{!bindings.openSubTask.enabled}"
                  action="toChild">
    <af:setActionListener from="#{row.Empno}"
                          to="#{pageFlowScope.Empno}"/>
  </af:commandLink>
</af:column>

Note that when the link is clicked:

  • actionListener and the action specified on the link are executed, in that order.

  • openSubTask is called only from the original parent task flow path (that is, initParent), not from the sub task flow path (that is, toChild).

The EmployeeDetails activity in Figure 15-7 is a Task Flow Call activity that invokes the ToChildSFFlow sub task flow. Before the sub task flow is executed, add initialization steps. These initialization steps could include, but are not limited to:

  • Set up parent states. For this example, set the selected employee's row to be the current row.

  • Set up the Contextual Area state.

  • Set up states to allow the sub task flow to navigate back to the parent task flow.

There are two approaches to setting up the initialization steps:

  • In the parent task flow

  • In the sub task flow

For the first approach, you can add logic to initialize both paths before the task flow call activity in the parent task flow. For the second approach, you initialize states in the sub task flow by using input parameters of the sub task flow. For example, the sub task flow will take an input parameter named Empno. In effect, the second approach just postpones the initialization to the sub task flow.

The definition of input parameters in the task flow call activity is:

<task-flow-call id="EmployeeDetails">
     <task-flow-reference>
       <document>/WEB-INF/oracle/apps/xteam/demo/ui/flow/ToChildSFFlow.xml</document>
       <id>ToChildSFFlow</id>
     </task-flow-reference>
     <input-parameter>
       <name>Empno</name>
       <value>#{pageFlowScope.Empno}</value>
     </input-parameter>
</task-flow-call>

Note that this means that the calling task flow needs to store the value of Empno in #{pageFlowScope.Empno}. For example, from the original parent task flow path, it is set to be #{row.Empno} using the setActionListener tag. For the sub task flow path, it is set using the parent task flow's input parameter Empno. On the sub task flow, you need to specify its input parameters as:

<task-flow-definition id="ToChildSFFlow">
   <default-activity>TochildSFPF</default-activity>
   <input-parameter-definition>
     <name>Empno</name>
     <value>#{pageFlowScope.Empno}</value>
     <class>java.lang.String</class>
   </input-parameter-definition>
   ...
</task-flow-definition>

Note that the name of the input parameter (Empno) must be the same as the parameter name defined on the task flow call activity. When the parameter is available, Oracle ADF will place it in #{pageFlowScope.Empno} to be used within the sub task flow. However, this pageFlowScope is different from the one defined in the task flow call activity because they have a different owning task flow (that is, parent task flow versus sub task flow).

The definition of the sub task flow is shown in Figure 15-8:

Figure 15-8 Example Sub Task Flow Definition

Described in the surrounding text.

In the sample implementation, you implemented the initialization step in the sub task flow. The Empno variable is passed as a parameter to the sub task flow and used to initialize the parent state. When the sub task flow is started, the default view activity (TochildSFPF) is displayed. Before it renders, the initPage method on the ChildSFBean will be executed. The page definition of the default page is defined as:

<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel">
 <parameters/>
 <executables>
   ...
   <invokeAction id="initPageId" Binds="initPage" Refresh="always"/>
 </executables>
 <bindings>
   ...
   <methodAction id="initPage" InstanceName="ChildSFBean.dataProvider"
                 DataControl="ChildSFBean" RequiresUpdateModel="true"
                 Action="invokeMethod" MethodName="initPage"
                 IsViewObjectMethod="false"
                 ReturnName="ChildSFBean.methodResults.initPage_ChildSFBean_dataProvider_initPage_result"/>
    ...
 </bindings>
</pageDefinition>

The initPage method is specified in the executables tag and will be invoked when the page is refreshed. The initPage method itself is defined as:

public void initPage()
{ 
    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExpressionFactory exp = facesContext.getApplication().getExpressionFactory();
    DCBindingContainer bindingContainer =
      (DCBindingContainer)exp.createValueExpression(
          facesContext.getELContext(),"#{bindings}",DCBindingContainer.class).getValue(facesContext.getELContext());
    ApplicationModule am = bindingContainer.getDataControl().getApplicationModule();
 
    ViewObject vo = am.findViewObject("ComplexSFEmpVO");
    vo.executeQuery();
 
    Map map = AdfFacesContext.getCurrentInstance().getPageFlowScope();
    if(map !=null){
         Object empObj = map.get("Empno");
         if(empObj instanceof Integer){
             Integer empno =(Integer)map.get("Empno");// new Integer(empnoStr);
             Object[] obj = {empno};
             Key key = new Key(obj);
             Row row = vo.getRow(key);
             vo.setCurrentRow(row);
         }
         else
         {
             String empnoStr = (String)map.get("Empno");
             Integer empno = new Integer(empnoStr);
             Object[] obj = {empno};
             Key key = new Key(obj);
             Row row = vo.getRow(key);
             vo.setCurrentRow(row);
         }
     }
}

The initPage method takes the input parameter Empno from #{pageFlowScope.Empno} as a key to select a row and set it to be the current row in the master Employee table.

15.2.4 How to Use Additional Capabilities of the Recent Items openSubTask API

The openSubTask API has additional capabilities. For example, consider an employee search page in which you enter parameters such as department number and manager ID, and search for the matching employee records. You can use the openSubTask API to register a search page with search parameters. The next time the user can see the search results by just starting it from the Recent Items menu. This is similar to using parametersList to specify search parameters while registering the search task flow. While starting, additional programming can be done to retrieve the search parameters and execute the query with the parameter values.

Favorites

As soon as tasks are recorded on the Recent Items list, they are eligible for Favorites. The Favorites menu is implemented on top of Recent Items. Any current task on the Recent Items list can be bookmarked and placed in the Favorites folders. Currently, only a one-level folder is supported. Similar to Recent Items, tasks on the Favorites list can be started directly from the list. So, the description in this section for Recent Items applies also to the Favorites implementation. For example, sub flows based on the design pattern described in this section can be registered on the Favorites list as well as the Recent Items list.

15.2.5 How to Implement Data Security for Recent Items and Favorites

Data security controls access to the data displayed on the target. When data security is enforced on the target page, any changes to the access that the user has is properly reflected on the data.

However, there are use cases in Oracle Fusion Applications where the Recent Items and Favorites features may cause security issues. In some cases there is a master/detail relationship between objects in which only the parent object has data security implemented. The child object has no data security enforced and is expected to inherit security from the parent. In such cases, the user can only navigate to the child task flow through the parent task flow. But because the child task flow can be added as a Recent Item or Favorite, users can navigate directly to the child through Recent Items, whether they still have access to the parent.

Data security ensures that user access is properly enforced on all flows that can be accessed from Recent Items or Favorites.

Implementation

Implementation consists of ensuring that security is enforced consistently in all scenarios and that authorization exceptions are handled by the standard central method.

Security Enforcement

For function security, no additional implementation is required. The recommendations for data security are:

  • Data security should be applied on an appropriate level. For example, in a master/detail relationship, security definitions should be created on both parent and child objects and applied on a proper level.

  • If the child record inherits the security from its parent, then the parent's security must be programmatically enforced on a sub flow. This should be done by adding a default method activity on the task flow and calling the data security API to check that the user has access to the parent. If the user does not have proper access, an authorization exception is generated.

    In addition, you should programmatically enforce the parent data security where applicable. For instance, if a view object takes the parent primary key as a parameter, before executing the query the code should check that the user has access to the parent.

15.2.6 Known Issues

  • Recent Items are persistent across user sessions.

  • You may see a null pointer exception when all the task flow parameters are not supplied values.

15.3 Implementing the Watchlist

The Watchlist is a portlet or menu, accessible to Oracle Fusion Applications users, that provides a summary of items that a user wants to track. The Watchlist includes seeded items (items that are provided out of the box) categorized by functional areas, and items created by the user. Technically, the Watchlist presents a list of pre-queried searches (saved searches or standard queries) of things the user wants to track. Each item is composed of descriptive text followed by a count. Each item also is linked to a page in a work area where the individual items of interest are listed.

The Watchlist is available both as a dashboard region in the Welcome tab of the Home dashboard, and as a global menu. These are two views of the same content. The dashboard region is available to users as soon as they log in, while the global menu is accessible as they navigate through the suite.

The Watchlist will be refreshed to fetch new counts and items whenever the user navigates to the Home page. The Watchlist can refresh the entire list or individual categories as needed. Users can personalize the Watchlist to hide or show items.

Figure 15-9 shows an example of the Watchlist portlet and menu.

Figure 15-9 Example Watchlist Portlet and Menu

Described in the surrounding text.

You have these high-level tasks:

  • Code view objects with bind variables and default values for bind variables as needed for calculating the Watchlist count. These view objects will be executed at runtime by the Watchlist API to get the Watchlist count for the user.

  • Code task flows to enable drill-down from the Watchlist UI.

  • Seed FND deployments to specify host, port, and context root information for UI drilldown and service invocation.

  • Seed FND standard lookups for each Watchlist category and item meaning.

  • Seed information to tell the Watchlist what counts to track, how to display them to the user, which view objects to execute, and how to drill down to the work area and tasks.

  • Import Watchlist JAR files as ADF libraries in service and UI projects.

  • Set up a service interface method so that product code and Watchlist code can interact.

15.3.1 Watchlist Data Model Effects

Product teams will seed data into the ATK_WATCHLIST_CATEGORIES and ATK_WATCHLIST_SETUP tables. Rows in the ATK_WATCHLIST_ITEMS will be managed by Watchlist code, but you will query it for testing verification.

The only other data model effect will be in the creation of summary tables. These summary tables help with retrieving the count of Watchlist items with data security. See Summary Tables.

15.3.2 Watchlist Physical Data Model Entities

The Watchlist data model is supported by ATK. The tables are:

  • ATK_WATCHLIST_CATEGORIES: Represents the functional categories in which each Watchlist item will fit. See Table 15-2.

  • ATK_WATCHLIST_SETUP: Represents a type of count that a Watchlist item can track. The primary key is a Watchlist item code. See Table 15-3.

Table 15-2 ATK_WATCHLIST_CATEGORIES

Column Name Datatype Required Comments

WATCHLIST_CATEGORY_CODE

VARCHAR2(100)

Yes

Primary Key - Unique code based on Product Code Prefix. Ensure that this code begins with <PRODUCT SHORT CODE>_, so that it does not overlap with others.

CATEGORY_LOOKUP_TYPE

VARCHAR2(30)

Yes

Reference to FND_STANDARD_LOOKUP_TYPES.LOOKUP_TYPE

Product teams will seed the lookup type with meaning for the category (VIEW_APPLICATION_ID = 0 and SET_ID = 0). The translated lookup type meaning is shown in the Watchlist UI for the category.

OWNING_MODULE_NAME

VARCHAR2(4000)

Yes

Reference to FND_APPL_TAXONOMY.MODULE_NAME for the owning product or module. This is used for seed data purposes.

OWNING_MODULE_ID

VARCHAR2(32)

Yes

Reference to FND_APPL_TAXONOMY.MODULE_ID for the owning product or module. This is used for seed data purposes.

OWNING_APPLICATION_ID

NUMBER

Yes

Reference to FND_APPL_TAXONOMY.ALTERNATIVE_ID for the owning product or module. This is used for seed data purposes.

REFRESH_SERVICE_ENDPOINT_KEY

VARCHAR2(60)

Yes

The key to determine the host, port, context root, and so on to construct the URL for the service end point (wsdl location).

This will be based on the Applications Core lookup API that will be used to determine the end point.

REFRESH_SERVICE_NAME

VARCHAR2(400)

Yes

The service that must be invoked for count calculation (for refreshing this category).

REFRESH_SERVICE_METHOD_NAME

VARCHAR2(400)

Yes

Obsolete. The hard-coded method name will be refreshWatchlistCategory. Product teams will create this hard-coded service method.

ENABLED

VARCHAR2(1)

Yes

Defaults to Y. Defines if this Watchlist category is enabled or active.

CREATED_BY

VARCHAR2(64)

Yes

Standard WHO Column

CREATION_DATE

TIMESTAMP

Yes

Standard WHO Column

LAST_UPDATED_BY

VARCHAR2(64)

Yes

Standard WHO Column

LAST_UPDATE_DATE

TIMESTAMP

Yes

Standard WHO Column

LAST_UPDATE_LOGIN

VARCHAR2(32)

Yes

Standard WHO Column

Table 15-3 ATK_WATCHLIST_SETUP

Column Name Datatype Required Comments

WATCHLIST_ITEM_CODE

VARCHAR2(100)

Yes

Primary Key - Uses the Category Code Prefix

ITEM_LOOKUP_CODE

VARCHAR2(30)

Yes if WATCHLIST_ITEM_TYPE != USER_SAVED_SEARCH

Reference to FND_LOOKUPS.LOOKUP_CODE

Product teams will seed the lookup code with the meaning for the parent category lookup type. The translated lookup meaning is shown in the Watchlist UI with the count appended.

CATEGORY_CODE

VARCHAR2(30)

Yes

None

PRIVILEGE_BASED

VARCHAR2(1)

Yes

Identification of a Watchlist item that is created against a security action instead of a specific user.

OWNING_PRIVILEGE_NAME

 

Yes if

PRIVILEGE_BASED = Y

Defines if this item is created against a security action. Users that have this action will be able to view this item.

FUNCTION_PRIVILEGE_NAME

VARCHAR2(400)

 

Defines the region action for this item's drilldown work area. This is the page definition for the drilldown view or jspx that is part of your jazn-data.xml file. The user needs this permission policy to view the item in the Watchlist UI.

WATCHLIST_ITEM_TYPE

VARCHAR2(30)

Yes

Defines the Watchlist item type and maps to the lookup. Valid values are:

SEEDED_QUERY (Seeded Query)

SEEDED_SAVED_SEARCH (Seeded Saved Search)

USER_SAVED_SEARCH (User-created Saved Search)

HUMAN_TASK (Worklist item)

HUMAN_TASK_DEF_ID

VARCHAR2(200)

Yes if WATCHLIST_ITEM_TYPE = HUMAN_TASK

Human Task Definition Identifier

HUMAN_TASK_STATE

VARCHAR2(100)

Yes if WATCHLIST_ITEM_TYPE = HUMAN_TASK

Human Task State Identifier.

REFRESH_AGE

NUMBER(9)

Yes if WATCHLIST_ITEM_TYPE != HUMAN_TASK

Defines the time for a count in seconds. After this many seconds have passed since the last refresh time, the Watchlist UI will issue a count recalculation request

VIEW_OBJECT

VARCHAR2(400)

Yes if WATCHLIST_ITEM_TYPE != HUMAN_TASK

Complete path of the view object that must be executed for count calculation.

SUMMARY_VIEW_ATTRIBUTE

VARCHAR2(400)

Yes if WATCHLIST_ITEM_TYPE != HUMAN_TASK and this is a summary view object

Indicates the view object attribute name if using a summary view object for Watchlist count calculation. On execution, this view object returns only one row with the Watchlist item count.

APPLICATION_MODULE

VARCHAR2(400)

Yes if WATCHLIST_ITEM_TYPE != HUMAN_TASK

Complete path of the application module that contains the view object instance that must be executed for count calculation.

AM_CONFIG_NAME

VARCHAR2(400)

Yes if WATCHLIST_ITEM_TYPE != HUMAN_TASK

The application module configuration name for creating an instance of the application module from code. This is typically AMLOCAL.

VIEW_OBJECT_INSTANCE

VARCHAR2(400)

Yes if WATCHLIST_ITEM_TYPE != HUMAN_TASK

The instance name for the view object in the application module.

VIEW_CRITERIA_ID

VARCHAR2(400)

Yes if WATCHLIST_ITEM_TYPE = SEEDED_SAVED_SEARCH

The view criteria that must be applied when executing the view object.

NAVIGATION_URL_KEY

VARCHAR2(60)

Yes

The key to determine the host, port, and context root to construct the URL for the UI drilldown for a Watchlist item.

This will be based on the Applications Core lookup API that is used for UI navigation across Java EE applications.

VIEW_ID

VARCHAR2(400)

Yes

The view ID (as per the UI Shell menu) for the workarea or page that contains the task flow for this Watchlist item's drilldown from the Watchlist UI.

PAGE_PARAM_LIST_STRING

VARCHAR2(400)

No

Parameters list for the page. If the target workarea page accepts page parameters, this is a semicolon-delimited string of name-value pairs.

TASKFLOW_ID

VARCHAR2(400)

Yes

The task flow for this Watchlist item's drilldown from the Watchlist UI.

TF_KEY_LIST_STRING

VARCHAR2(400)

No

Key list to pass into the task flow to open in the target workspace. This is a semicolon-delimited list of keys or key-value pairs. For example, "key1;key2=value2"

TF_PARAMETER_STRING

VARCHAR2(400)

Yes if WATCHLIST_ITEM_TYPE IN (SEEDED_SAVED_SEARCH, USER_SAVED_SEARCH)

Parameters list to pass in to the task flow to open in the target workspace. This is a semicolon-delimited string of name-value pairs. For example, "param1=value1;param2=value2"

For a user-created saved search, the view criteria ID will be appended to this string (the string must end with <paramName>= for saved search).

TASK_TAB_LABEL

VARCHAR2(400)

Yes

Label for the task flow to open in the target workspace.

ENABLED

VARCHAR2(1)

Yes

Defaults to Y. A definition that indicates if this Watchlist category is enabled or active.

CREATED_BY

VARCHAR2(64)

Yes

Standard WHO Column

CREATION_DATE

TIMESTAMP

Yes

Standard WHO Column

LAST_UPDATED_BY

VARCHAR2(64)

Yes

Standard WHO Column

LAST_UPDATE_DATE

TIMESTAMP

Yes

Standard WHO Column

LAST_UPDATE_LOGIN

VARCHAR2(32)

Yes

Standard WHO Column

15.3.3 Supported Watchlist Items

Supported Watchlist items are all asynchronous (that is, queries are executed on demand when the user requests a refresh or just before the Watchlist UI is shown). There are four types of asynchronous Watchlist items (watchlist_item_type):

  • Seeded queries (SEEDED_QUERY)

  • Seeded saved searches (SEEDED_SAVED_SEARCH)

  • User-created saved searches (USER_SAVED_SEARCH)

  • Human task-flow items (HUMAN_TASK)

15.3.3.1 Asynchronous Items Overview: Expense Reports Saved Search

For asynchronous Watchlist items, the count is updated only upon request. For example, in Expenses, there is an expense reports search panel from which users can make searches and save them in MDS for future use. Users should be able to promote their saved searches to the Watchlist for tracking. This Watchlist item (of type USER_SAVED_SEARCH) is asynchronous in that the Watchlist count will be updated only upon request, and events that change the count will not be updating the Watchlist simultaneously. In this case, Watchlist code is responsible for querying the count of an asynchronous Watchlist item on demand.

For the Expense report saved search panel example:

  • Because this item is of type USER_SAVED_SEARCH, Watchlist items are created when the user promotes a saved search on the search panel in Expenses. This invokes a Watchlist service on the Watchlist that does the Watchlist item creation. This is not needed for Watchlist items of type SEEDED_QUERY or SEEDED_SAVED_SEARCH.

  • A request to refresh the Watchlist item count comes from the Watchlist portlet. This will invoke an exposed service, which delegates the call to a method on a provided Watchlist JAR file.

  • The method on the Watchlist JAR file will take care of rerunning the query (on the expenses database) and taking the results to update the Watchlist item count (on the Watchlist database).

15.3.3.2 Summary of Implementation Tasks

At a high level, for asynchronous Watchlist items, your development tasks are:

  • Determine or set up view objects to execute the query for Watchlist count. You may want to include view criteria with bind variables and specify default values for bind variables. (Not needed for Human Task)

    For example, most view objects seeded for the Watchlist would filter by user to show counts specific to the logged-in user. You could create a view criteria with a bind variable called userId and specify the default value as a groovy expression to determine the current user ID from the security context. You then would seed this view criteria ID in the setup table along with the view object. The Watchlist API would execute the view object by applying this view criteria to get the row count. Example 15-2 shows code from the view object xml for a bind variable with a default value.

  • (Optional) Set up a summary view object to facilitate a refresh count and specify the summary attribute in the Watchlist setup table. Watchlist code would use this information to query the count instead of doing a rowcount on the executed view object results.

  • Include Watchlist model JAR files in your service project and set up a refreshWatchlistCategory service method. This method only will contain code to delegate the call to the nested Watchlist application module method that actually executes the view object queries by reading your Watchlist setup data. This requires that all the corresponding model projects are included as JAR files in this service project so view objects are available in the class path. This service should be exposed so that the Watchlist UI can use it to start the refresh process.

  • Determine and code task flows for drilldown from the Watchlist UI. There is no special coding required for these task flows; the key-value parameter string that you specify in the setup table will be used as the input parameter list when invoking the specified task flow for the UI drilldown on Watchlist items.

  • (Optional) Enable saved search promotion to the Watchlist. Include Watchlist model JAR files in the corresponding project for the query panel and work with the Watchlist API. For promotion and demotion of user-saved searches, code must invoke a method in the provided Watchlist JAR, which then will handle interaction back to the Watchlist. Add promotion and demotion components on the search panel so that saved searches can be used as Watchlist items.

  • Include Watchlist UI and Protected model JAR files in your UI SuperWeb project, to enable Watchlist menu drilldown in the UI Shell Global Area, as shown in Figure 15-10.

    Figure 15-10 Example Watchlist Menu Drilldown

    Described in the surrounding text.
  • Create FND_LOOKUPS for the displayed Watchlist category and item meaning (FND_STANDARD_LOOKUP_TYPES.LOOKUP_TYPE). Product teams must seed the lookup type with the meaning for the category (VIEW_APPLICATION_ID = 0 and SET_ID = 0). The translated lookup type meaning is shown in the Watchlist UI for the category, while the corresponding lookup value meanings are shown in the Watchlist UI for items (user saved search item meanings come from the saved search directly). Product teams must create seed data for this lookup.

  • Example 15-3 presents sample code to create or update the lookup.

Seed Watchlist categories and setup information. Create your category in ATK_WATCHLIST_CATEGORIES and then seed your items in the reference data table (ATK_WATCHLIST_SETUP). The watchlist_item_type determines how the Watchlist code will handle the item.

Note:

HUMAN_TASK items only require seeding in the tables to work; there are no view objects to create.

Example 15-2 Example Code from the View Object XML for Bind Variable with Default Value

<Variable
            Name="userId"
            Kind="where"
            Type="java.lang.String">
            <TransientExpression><![CDATA[return adf.context.securityContext.userName;]]></TransientExpression>
          </Variable>

Example 15-3 Sample Code to Create or Update Watchlist Lookup

declare
begin
FND_LOOKUP_TYPES_PKG.CREATE_OR_UPDATE_ROW (
  X_VIEW_APPSNAME => 'FND',
  X_LOOKUP_TYPE => 'FIN_EXM_WATCHLIST_CATEGORY',
  X_APPLICATION_SHORT_NAME => 'EXM',
  X_MEANING => 'Expenses',
  X_DESCRIPTION => 'Expenses Watchlist Category',
  X_REFERENCE_GROUP_NAME => null
);
end;
 
declare
begin
FND_LOOKUP_VALUES_PKG.CREATE_OR_UPDATE_ROW (
  X_LOOKUP_TYPE           => 'FIN_EXM_WATCHLIST_CATEGORY',
  X_VIEW_APPSNAME         => 'FND',
  X_LOOKUP_CODE           => 'SAVED_EXPENSE_REPORTS',
  X_MEANING               => 'In Progress Expense Reports'
--  X_SET_CODE              IN VARCHAR2 DEFAULT NULL,
--  X_DESCRIPTION           IN VARCHAR2 DEFAULT NULL,
--  X_ENABLED_FLAG          IN VARCHAR2 DEFAULT 'Y',
--  X_START_DATE_ACTIVE     IN VARCHAR2 DEFAULT NULL,
--  X_END_DATE_ACTIVE       IN VARCHAR2 DEFAULT NULL,
--  X_DISPLAY_SEQUENCE      IN NUMBER DEFAULT NULL
  );
end;

15.3.4 How to Use the Watchlist

Follow the procedures described in this section to use the Watchlist.

15.3.4.1 Make the Watchlist Link in the UI Shell Global Area Work

To ensure the Watchlist link works in your pages, complete these steps.

  1. Add this ADF Library JAR file to the SuperWeb user interface project for your application (the JAR file must be part of your Web Archive (WAR) in the WEB-INF/lib directory):

    fusionapps/jlib/AdfAtkWatchListPublicUi.jar

  2. Add this dependent model ADF Library JAR file in your application (the JAR file must be part of your Enterprise Archive (EAR) in the APP-INF/lib directory):

    fusionapps/jlib/AdfAtkWatchListProtectedModel.jar

  3. Add this dependent resource bundle ADF Library JAR file in your application (the JAR file must be part of your EAR in the APP-INF/lib directory):

    fusionapps/jlib/AdfAtkWatchListPublicResource.jar

  4. Add these resource-ref entries to the web.xml file in your SuperWeb user interface project:
    <resource-ref>
        <res-ref-name>wm/WorkManager</res-ref-name>
        <res-type>commonj.work.WorkManager</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>
    <resource-ref>
        <res-ref-name>jdbc/ApplicationDBDS</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>
    

15.3.4.2 Seed Reference Data (All items)

See Watchlist Physical Data Model Entities for details of the entire Watchlist data model. Seed only ATK_WATCHLIST_CATEGORIES and ATK_WATCHLIST_SETUP.

15.3.4.3 Create a Summary View Object (SEEDED_QUERY)

  • By default, Watchlist code will access the application module or view object that is specified in the setup table, and rerun the query to refresh the Watchlist count. The summary view object is one way to get the count. Usually this will be for efficiency reasons.

  • The product team can signal that it wants the Watchlist code to use its summary view object by seeding something in SUMMARY_VIEW_ATTRIBUTE of ATK_WATCHLIST_SETUP. If this is not null, the Watchlist code will get the view object, but instead of running the query, it will take only the first row and get the specified attribute.

  • Your task is to create a view object with the correct count in the attribute specified in the setup table.

15.3.4.3.1 Summary Tables

One common reason to use a summary view object is if the seeded query is based on Multiple-Organization Access Control (MOAC) data security. This is because you can calculate the count of this query for each Business Unit ID (BUID). The count of a user is just the sum of the counts of the BUIDs that the user can access.

For example, say you have a table (or query) that has three rows of BUID #1, three rows of BUID #2, and three rows of BUID #3. The current user has access to BUIDs #1 and #2.

If you wanted to get the count, MOAC would filter the rows by BUID and return six rows.

However, because you are interested only in the count, a more efficient way would be to create a summary table for this table. The summary table keeps track of the count for each BUID. For the example table, the summary table would resemble Table 15-4.

Table 15-4 Example Summary Table

BUID Count

1

3

2

3

3

3

Now, instead of using MOAC to find a count for a particular user, you use MOAC to find the sum of counts for the user. The example user would get the first two rows returned, and you can calculate the total count by summing the count column.

You can use summary tables to populate the summary view object attribute.

15.3.4.4 Create Seeded Saved Searches in MDS (SEEDED_SAVED_SEARCH)

In addition to seeding seeded saved searches in the reference tables, the saved searches need to be seeded in MDS so that when the user visits the saved search panel, the saved search will show as one of the choices in the drop-down box. Saved searches in MDS are stored in the file system as an XML file for each view object. The steps to create this file are:

  1. Ensure you have enabled MDS for your application.
  2. Run the application and visit the desired search page.
  3. Use the UI to create saved searches and name them. For each saved search, select Run Automatically.
  4. Examine the adf-config.xml file to determine where your file-based MDS repository is located.
  5. In a terminal, change to the directory:

    <MDS repository>/persdef/oracle/apps/.../mdssys/cust/user/<user>/

    The <user> directory would be the user you used to save the search with, or anonymous by default.

  6. Inside that directory, there should be an xxxVO.xml.xml file that contains the created saved searches. Keep that file.
  7. In that XML file, note the saved search ID, which should match the View Criteria ID in the reference data.

    Note:

    This ID can be different than the display name that you entered in the saved search UI.

15.3.4.5 Creating Application Module and View Objects (All except HUMAN_TASK)

15.3.4.6 Setting Up Service (All except HUMAN_TASK)

For the same Watchlist items, the Watchlist portlet must be able to invoke a refresh. You will set up and expose a service that includes local JAR files for this purpose. The nested Watchlist JAR file can use those local JAR files in its refresh code.

The service will expose the refreshCategory method that will delegate the call to the same method in the nested Watchlist application module. This method will be provided in the Watchlist JAR file and will contain code to perform the category-wide refresh.

15.3.4.7 Importing All Watchlist-Related Application Modules

Your Service project must import the other JAR files from your product that must be used by the Watchlist code.

15.3.4.8 Nesting Watchlist Application Modules

Include AdfAtkWatchListProtectedModel.jar and AdfAtkWatchListPublicModel.jar in your service project, and nest AdfAtkWatchListPublicUi.jar in your service application module.

Set up the AppMasterDB connection that comes with it. Point it to the database where you have seeded your data in the Watchlist tables. This usually is your development database that is used for the ApplicationDB connection.

15.3.4.9 Using the refreshWatchlistCategory Method

This method, shown in Example 15-4, refreshes all Watchlist items in the corresponding category.

Example 15-4 Refreshing Watchlist Items

public void refreshWatchlistCategory(String categoryCode) {
    AtkWatchlistPublicAMImpl wlAM = this.getAtkWatchlistPublicAMImpl();
    wlAM.refreshWatchlistCategory(categoryCode);
}

15.3.4.10 Importing Watchlist JAR Files into the Saved Search Project (USER_SAVED_SEARCH)

For subsequent steps that require running Watchlist APIs from your code, you must import the Watchlist JAR files. These also contain an AppMasterDB connection that must point to the Watchlist database.

  • Add AdfAtkWatchlistProtectedModel.jar and AdfAtkWatchlistPublicService.jar (fusionapps > jlib) as ADF libraries in the appropriate data model projects, preferably in a model project that is visible to both service and user interface model project application modules.

  • Add AdfAtkWatchlistPublicUi.jar as an ADF library to your user interface project.

  • Configure the AppMasterDB connection.

15.3.4.11 Promote Saved Search to the ATK Watchlist (USER_SAVED_SEARCH)

Every Watchlist-enabled saved search panel must include a component to let the user control which of the saved searches to promote. The pre-seeded saved searches will be shown as static, while the user can use checkboxes to determine which saved searches should be promoted. There will be listeners to this component that will publish business events to make the appropriate worklist changes.

Before You Begin

Ensure that the following steps have been performed.

  • Populate ATK tables with SEED watchlist category information. Product teams need to provide information about the service that refreshes the watchlist item count.

  • Populate ATK tables with appropriate watchlist setup information. There must be a watchlist setup item of type USER_SAVED_SEARCH.

  • Ensure the application is MDS enabled so users can save their searches and that the saved searches exist across sessions.

  • From the watchlist UI, users can drilldown to the transactional UI flows. Ensure that the transactional UI flow is properly set up so that it can show the search page when the user clicks the Watchlist item in the Watchlist UI.

  • In the application project, create a backing bean and register it as a backingBeanScope bean. In the backing bean, create the Java method shown in Example 15-5:

Example 15-5 Creating the Backing Bean

import oracle.adf.view.rich.model.QueryDescriptor;
import oracle.adf.view.rich.model.QueryModel;
..................
 
    public List<String> getWatchListUserSavedSearchList() {
        if (AppsLogger.isEnabled(AppsLogger.FINEST)) {
          AppsLogger.write(this, "Watchlist saved search promotion: Entering method getWatchListUserSavedSearchList", AppsLogger.FINEST);
        }
        List<String> WatchListUserSavedSearchList = new ArrayList<String>();
        QueryModel queryModel =
            (QueryModel)evaluateEL("#{bindings.ImplicitViewCriteriaQuery.queryModel}");//See next code sample for evaluateEL()
        if (queryModel != null) {
          if (AppsLogger.isEnabled(AppsLogger.FINEST)) {
            AppsLogger.write(this, "Watchlist saved search promotion: getWatchListUserSavedSearchList method: queryModel is not null", AppsLogger.FINEST);
          }
            List userQueries = queryModel.getUserQueries();
            if (userQueries != null & userQueries.size() > 0) {
              if (AppsLogger.isEnabled(AppsLogger.FINEST)) {
                AppsLogger.write(this, "Watchlist saved search promotion: getWatchListUserSavedSearchList method: User Saved Searches exist", AppsLogger.FINEST);
              }
                for (int i = 0; i < userQueries.size(); i++) {
                    QueryDescriptor qd = (QueryDescriptor)userQueries.get(i);
                    if(qd != null){
                      if (AppsLogger.isEnabled(AppsLogger.FINEST)) {
                        AppsLogger.write(this, "Watchlist saved search promotion: getWatchListUserSavedSearchList method: Adding user saved search name to the watchListUserSavedSearchList using QueryDescriptor getName: " + qd.getName(), AppsLogger.FINEST);
                      }
                      WatchListUserSavedSearchList.add(qd.getName());
                    }
                }
            }
        }
        if (AppsLogger.isEnabled(AppsLogger.FINEST)) {
          AppsLogger.write(this, "Watchlist saved search promotion: Exiting method getWatchListUserSavedSearchList: returning watchListUserSavedSearchList: " + WatchListUserSavedSearchList, AppsLogger.FINEST);
        }
        return WatchListUserSavedSearchList;
    }     
15.3.4.11.1 How to Promote a User-Saved Search to the Watchlist

Follow these steps to integrate the ATK task flow to promote saved searches to the Watchlist.

Note:

If you already have implemented promoting the user-saved search to the Watchlist, see Additional Steps for Existing Consumers.

  1. Ensure the AdfAtkWathcListPublicUi.jar file is available (usually in the fusionapps/jlib directory).

  2. Start JDeveloper and open the .jspx or .jsff page containing the query region whose saved searches have to be promoted.

  3. In the query region, add a toolbar facet.

    1. Right-click the component.

    2. In the menu that opens, select Facets-Query.

    3. From the submenu, select Toolbar, as shown inFigure 15-11.

      Figure 15-11 Adding Toolbar Facet to Query Region

      Described in the surrounding text.
  4. In the toolbar facet of the query region, drag and drop an ADF Toolbar component, such as Toolbar (ADF Faces.Common Components) shown in Figure 15-12, onto the page.

    Figure 15-12 Adding an ADF Toolbar Component

    Adding an ADF Toolbar Component

    The toolbar facet in the Source view will look similar to:

    <f:facet name="toolbar">
      <af:group id="g1">
        <af:toolbox id="t1">
          <af:region value="#{bindings.AtkWatchlistUserSavedSearchPromotionTF1.regionModel}"
                     id="r7"/>
        </af:toolbox>
        <af:toolbar id="t2"/>
      </af:group>
    </f:facet>
    
  5. Open the Resource Palette and create a File System connection to the directory containing the AdfAtkWatchListPublicUI.jar file, as shown in Figure 15-13.

    Figure 15-13 Creating the File System Connection

    Described in the surrounding text.
  6. Expand the connection node and the ADF library node in the Resource Palette as shown in Figure 15-14.

    Figure 15-14 Expanding the Connection Node

    Described in the surrounding text.

    When the ADF Task Flows node is expanded, you should see two task flows. The task flow AtkWatchlistUserSavedSearchPromotionTF is the one to be used.

  7. Drag and drop the AtkWatchlistUserSavedSearchPromotionTF task flow as a region into the toolbar component (present in the query region toolbar facet), created in the previous steps. As soon as the task flow is dropped onto the page, the Edit Task Flow Binding dialog is displayed. Enter the following values for the mandatory parameters.

    • categoryCode: Provide the WATCHLIST_CATEGORY_CODE that has been seeded in the ATK tables.

    • watchlistItemCode: Provide the WATCHLIST_ITEM_CODE provided while creating the Watchlist setup data.

    • userSavedSearchList: This represents the model object of the query region. To populate this field:

      1. Select the userSavedSearchList input field, click the small "v" icon present at the end of the field and select the Expression Builder option.

      2. Select the value from the Java method shown in Example 15-5. In this case, the value is watchListUserSavedSearchList, found in ADF Managed Beans > backingBeanScope > searchOrderScheduleBackingBean > watchListUserSavedSearchList.

        The Expression will be:

        #{backingBeanScope.searchOrderScheduleBackingBean.watchListUserSavedSearchList}
        
      3. Click OK to insert the value in the userSavedSearchList field.

    • internalCriteriaName: This represents the ViewCriteria Name of the search binding executable of the query region present in the UI page. To populate this field, follow the same steps as you did to populate the userSavedSearchList field, but select internalCriteriaName. Also see Additional Steps for Existing Consumers.

    When you are finished, the Edit Task Flow Binding dialog will resemble Figure 15-15.

    Figure 15-15 Completed Edit Task Flow Binding Dialog

    Described in the surrounding text.
  8. Click OK. This creates a region component in the UI page, as shown in Figure 15-16.

    Figure 15-16 Created Region Component

    Described in the surrounding text.
  9. Open the page definition file and select the executable associated with the Watchlist-related task flow.

  10. Open the Property Inspector and set the Refresh field to ifNeeded, as shown in Figure 15-17. The ATK saved search promotion task flow has to be refreshed each time the query model changes. This step ensures that the task flow is refreshed whenever the query model changes.

    Figure 15-17 Setting Refresh to ifNeeded

    Described in the surrounding text.
  11. Add a task flow security permission to the AtkWatchlistUserSavedSearchPromotionTF task flow in the jazn-data.xml file.

    1. Open the jazn-data.xml file and select the ADF Policies tab.

    2. From the Task Flow list, select AtkWatchlistUserSavedSearchPromotionTF, grant it to an appropriate role, and select appropriate actions as shown in Figure 15-18.

      Figure 15-18 Adding Security Permissions to jazn_data.xml

      Described in the surrounding text.
  12. Run the UI page. In the toolbar facet of the query region, there will be a Watchlist Options button, as shown in Figure 15-19.

    Figure 15-19 Watchlist Options Button in Toolbar Facet

    Described in the surrounding text.

    When you click the button, a popup with the list of all saved searches is displayed, as shown in Figure 15-20.

    Figure 15-20 List of Saved Searches

    Described in the surrounding text.

Additional Steps for Existing Consumers

If you already have implemented promoting Saved Search to the ATK Watchlist, there are four additional steps.

  1. In your pageDef, change the WatchList task flow parameter called queryModel from:

    <parameter id="queryModel"     value="#{bindings.ExistingCriteria.queryModel}"/>
    

    to:

    <parameter id="userSavedSearchList"
      value="#{backingBeanScope.YourBean.watchListUserSavedSearchList}"/>
    
  2. Use the public List<String> getWatchListUserSavedSearchList() Java method by passing the QueryModel binding of your af:query.

  3. Change the old queryBinding parameter to the new internalCriteriaName parameter. For example, change:

    parameter id="queryBinding" value="#{bindings.SearchPageVOCriteriaQuery}"
    

    to:

    parameter id="internalCriteriaName" value="#{backingBeanScope.searchRelatedBean.internalCriteriaName}"
    
  4. In someBackingBeanScopeBean.java, such as the one you created in Example 15-5, add the two methods shown in Example 15-6.

    Note: In someBackingBeanScopeBean.getInternalCriteriaName(), the queryBinding variable in the first line is the one you see in af:query. For example, af:query queryListener="#{bindings.SearchPageVOCriteriaQuery.processQuery}" Just take the QueryBinding, #{bindings.SearchPageVOCriteriaQuery}.

Example 15-6 Additions to the backingBeanScopeBean

import javax.el.ExpressionFactory;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.context.FacesContext;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.jbo.uicli.binding.JUSearchBindingCustomizer;
 
  public Object evaluateEL(String expr)
  {
      FacesContext facesContext=FacesContext.getCurrentInstance();
      ExpressionFactory exprFactory=facesContext.getApplication().getExpressionFactory();
      ValueExpression valueExpr=exprFactory.createValueExpression(facesContext.getELContext(), expr, Object.class);
      return valueExpr.getValue(facesContext.getELContext());
  }
 
  public String getInternalCriteriaName() {
     if (AppsLogger.isEnabled(AppsLogger.FINEST)) {
       AppsLogger.write(this, "Watchlist saved search promotion: Entering method getInternalCriteriaName", AppsLogger.FINEST);
     }
     String queryBinding = "#{bindings.SearchPageVOCriteriaQuery}";
     DCBindingContainer  searchBinding = (DCBindingContainer)evaluateEL(queryBinding);       
     internalCriteriaName = JUSearchBindingCustomizer.getCriteriaName(searchBinding);
     if (AppsLogger.isEnabled(AppsLogger.FINEST)) {
       AppsLogger.write(this, "Watchlist saved search promotion: Exiting method getInternalCriteriaName with return value for internalCriteriaName: " + internalCriteriaName, AppsLogger.FINEST);
     }     
     return internalCriteriaName;
  } 

15.3.4.12 Code Task Flows to Accept Parameters (All except HUMAN_TASK)

If you have seeded the information properly, your normal task flows should work when expanded into from the Watchlist portlet.

15.3.4.12.1 Saved Search

For saved search Watchlist items, you will want the drilldown to load a specific saved search by default.

One function of the Watchlist portlet will be to take a user to the corresponding action area when the user clicks a Watchlist item. For saved searches, the desired functionality is to open the task flow containing the saved search panel and by default, show the clicked saved search.

On the portlet, upon clicking the link, code will put the proper ViewCriteria name into the PageFlowScope with the parameter name vcName.

When loaded, the destination task flow will have two tasks:

  • Look into the PageFlowScope to retrieve the ViewCriteriaName.

  • Obtain the RichQuery object, and apply the ViewCriteriaName to it, if one is given.

You will be concerned with implementing the two steps for the destination task flow. First, you can retrieve the ViewCriteriaName from the PageFlowScope with the code shown in Example 15-7.

Second, you can use the code shown in Example 15-8 to apply the ViewCriteria to the search panel. If no ViewCriteria was passed, the code loads the default ViewCriteria.

This code should be run once upon loading the destination task flow. One solution is to connect it to the rendered property of the search panel, and use a static variable to ensure that it only runs once.

Example 15-7 Retrieving the ViewCriteriaName from the PageFlowScope

Map pfs = RequestContext.getCurrentInstance().getPageFlowScope(); String vcName = (String) pfs.get("vcName");

Example 15-8 Applying ViewCriteria to the Search Panel

if (vcName == null || vcName.equals("")) {
        // If no ViewCriteria is given, load default VC
        DCBindingContainer dcbc = 
(DCBindingContainer)BindingContext.getCurrent()
                .getCurrentBindingsEntry();
FacesCtrlSearchBinding fcsb = 
(FacesCtrlSearchBinding)dcbc
.findExecutableBinding("ImplicitViewCriteriaQuery");
FacesCtrlSearchDef def = (FacesCtrlSearchDef)fcsb.getDef();
DCParameterDef paramDef = 
(DCParameterDef)def.getParameterDef(
JUSearchBindingCustomizer.PARAMCRITERIA);
fcsb.evaluateParameter(paramDef.getExpression(),false));
} else {
        QueryModel model = search_query.getModel();
        QueryDescriptor selDescriptor = model.create(vcName, null);
if (selDescriptor != null) {
model.setCurrentDescriptor(selDescriptor);
}
BindingContainer bindings = 
BindingContext.getCurrent().getCurrentBindingsEntry();
OperationBinding method = (OperationBinding) 
bindings.getControlBinding("applyViewCriteriaByName");
method.getParamsMap().put("name", vcName);
method.execute();
}

15.3.4.13 Import Watchlist UI JAR File in User Interface Project

There is a link in the UI Shell for Watchlist in the global area. To make it work, the user interface project must include these Watchlist UI JAR files: AdfAtkWatchListPublicUI and its dependent model JAR file AdfAtkWatchListProtectedModel.

15.3.4.14 Additional Entries for Standalone Deployment

These entries are required for the Watchlist service and UI to be able to work in a standalone deployment.

  • Add this entry in the ejb-jar.xml file in your service project that contains the watchlist service next to the similar entry for ApplicationDB. You need resource-ref entries for both ApplicationDB and AppMasterDB:

    <resource-ref> 
      <res-ref-name>jdbc/AppMasterDBDS</res-ref-name> 
      <res-type>javax.sql.DataSource</res-type> 
      <res-auth>Container</res-auth> 
    </resource-ref>
    
  • Add this entry in the web.xml file in your SuperWeb project next to the similar entry for ApplicationDB. You need resource-ref entries for both ApplicationDB and AppMasterDB:

    <resource-ref> 
      <res-ref-name>jdbc/AppMasterDBDS</res-ref-name> 
      <res-type>javax.sql.DataSource</res-type> 
      <res-auth>Container</res-auth> 
    </resource-ref>
    
  • The connections.xml file should have a valid database entry for AppMasterDB.

15.4 Implementing Group Spaces

Group Spaces bundle all the collaboration tools and provide an easy way for users to create their own ad hoc collaborative groups around a project or business artifact.

This section describes how to implement the Group Spaces functionality that is available in UI Shell.

Assumptions

These assumptions are made:

  • The implementation is occurring in a label that is either dependent on ATGPF_MAIN or uptakes ATGPF_MAIN on a regular basis.

  • The spaces application and the JDeveloper Standalone WebLogic Server that would run the application have the requisite setup done.

  • The consuming applications are secure. Group Spaces functionality attempts to retrieve the group spaces for the logged-on user. Without a secure application, this functionality would fail.

15.4.1 How to Implement Group Spaces

Follow these steps to implement Group Spaces.

  1. Ensure the Oracle WebCenter Portal Spaces Client library, spaces-webservice-client.jar, has been added to the Project.

  2. Define an application connection to point to the URL of the WebCenter Portal Spaces WebService. To do this:

    1. Right-click Connections in the Application Resource palette.

    2. Select New Connection > URL.

    3. Enter the value $HOST:$PORT/webcenter/SpacesWebService, where $HOST and $PORT are the hostname and the port on which the spaces application is running.

    4. Save this connection with the name SpacesWebServiceEndpoint.

  3. To make a homepage tab appear within UI Shell, deploy the Functional Setup Manager application.

15.4.2 Overview of Group Spaces Functionality

The Group Spaces functionality implements these features:

  • When the Group Spaces link is clicked, a popup displays the logged-in user's Group Spaces.

  • When the user clicks a Group Space from the list, the Group Space's home page is opened in an iFrame. This iFrame is rendered within a Home Page tab called WebCenter. The Group Space is opened suppressing the WebCenter Portal chrome but will still render all the tabs within that Group Space. (Note: Chrome is a term for the visible graphical interface features of an application.) This chrome level suppresses the WebCenter Portal chrome but will still render all the tabs within that Group Space.

  • When the user clicks View All Group Spaces, the same UI as the My Group Spaces in the spaces application is rendered. This is also rendered as an iFrame within the WebCenter Portal HomePage tab where it suppresses the chrome as well as the top level WebCenter Portal tabs.

15.4.3 How to Pass a Chromeless Template

When navigating to the WebCenter Portal home page, a WebCenter Portal template that does not contain the header chrome is desired. This can be done by appending &wc.pageTemplate=oracle.webcenter.spaces.siteTemplate.gsContent.

Pass this parameter when navigating to a Group Space from the Group Spaces global dialog, Tag Center, Global Search, or an Activity Stream link.

Use a question mark (?) instead of an ampersand (&) if this is the only request parameter for that URL.

15.5 Implementing Activity Streams and Business Events

Activity Streams is a feature provided by the WebCenter Portal. Use Activity Streams to capture changes and publish Activity messages. Customer Relations Management (CRM), in particular, makes heavy use of this feature to keep abreast of service requests and opportunities. Users subscribe to Activity Streams by using the Activity Streams user interface.

For business events, Activities are shown only to users who subscribe to the stream and who have the necessary security access.

Activity Streams can be connected to:

  • Business Objects. At a high level, business objects correspond to a workarea, such as Sales or Contacts.

  • Group Spaces. For example, a team lead can set up a Group Space that team members can use to share information about a project.

  • People Connection. This is similar to the various social networking sites on the Internet that let people interact with friends and business associates.

This section is concerned only with business objects.

15.5.1 Introduction to WebCenter Portal Activities

A WebCenter Portal Activity is comprised of the following:

  • Actor - The user who performed the action that triggered the business event. For Oracle Fusion Applications, this will be the userid fetched from the user session.

  • ActivityType - The type of Activity to be published. This defines the format of the Activity message.

  • Objects - The objects associated with the Activity. There could be multiple objects associated with an Activity, but for business events, only the event source is used as an object.

WebCenter Portal Activities are defined in the service-definition.xml file. The scope of service_definition.xml is per business object. The service ID attribute should match the name of the entity object. The service-definition.xml file contains ActivityTypes, ObjectTypes and resource-view definitions. An ActivityType must be defined for every business event on the entity object. The type name should match the business event's name. The messageFormatKey attribute in the ActivityType element points to a message key in a Resource Bundle. It defines the format of the message displayed in the Activity Stream UI. These tokens are supported in a message.

  • {actor[0]}: Replaced by the display name of the user who triggered the event.

  • {object[0]}: Replaced by the value of the attribute in the event payload whose attribute name matches the object type name.

  • {custom[attr1].value}: Replaced by the value of the attr1 attribute in the event's payload.

The message format would look similar to:

{actor[0]} updated Opportunity {object[0]} status to {custom[status].value}

15.5.2 How to Publish Business Events to Activities

ADF Business Components business events are, by default, published to SOA Event Delivery Network (EDN). Applications Core implements a BusinessEventAdapter to listen to these events, transition them to Activities, and asynchronously publish them to the ActivitiyStream Service. This adapter is a singleton per application and publishes the business events raised to the ActivityService in the order they are produced. A business event is published as an Activity only when an ActivityType matching the name of the event is found in the service definition for the business object.

While mapping a business event to an Activity, keep these notes in mind:

  • There is one-to-one mapping between an Activity Service definition and a business object. The service ID attribute in the service-definition.xml file should match the Entity name.

  • The ActivityType name should match the name of the business event.

  • Define an ObjectType with the name attribute matching an attribute name in the payload. This attribute value will replace the {object[0]} token in the message format. WebCenter Portal supports multiple object types for an Activity, but for business events-related Activities, only one Object that corresponds to the event source is supported. The Object type name should match the name of the attribute whose value should be displayed in the hyper link for the object.

  • Define a message format using tokens for Actor, Object and customAttributes. The custom attribute names used in the token should match the attribute names in the payload.

  • In the Activity message displayed in the UI, only Actor and Object display values will be rendered as hyper links. If an Activity involves multiple objects, hyper links will be supported only for the event source object. Multiple attributes from the event payload can be referenced in the message.

  • The hyper link for the object allows users to navigate to the business object's work area. The target page for navigation can be configured through the resource-view element in the service-definition.xml file.

15.5.3 How to Publish Activities Using a Programmatic API

For certain scenarios, Oracle Fusion Applications are required to publish Activities for model changes that are not based on entity objects. Since Oracle ADF Business Components business events are based on entity objects, it is not possible to use these events for publishing Activities for non entity object-based model changes. For such scenarios, product teams could use the Applications Core API shown in Example 15-9 to programmatically publish Activities to the ActivityStream service.

BusinessActivityPublisher

This class provides the publishActivity API that can be used to publish Activities asynchronously. This is a singleton per Java EE application. An instance of this class can be obtained using the getInstance API. This lets product teams define such things as ActivityTypes, ObjectTypes, and resource-view definitions, declaratively in the service-definition.xml file, similar to business event-related activities.This would allow product teams to follow the same mechanism to define and publish Activities for both entity object and non-entity object-based model changes with very little code changes.

BusinessActivity Class

This is an abstract class that is used to represent an Activity corresponding to a business event. BusinessActivityPublisher:publishActivity() takes an instance of this class as a parameter. You would implement an instance of this class to encapsulate the details of the Activity corresponding to the non entity object-based model changes and invoke the publishActivity API with this as a parameter. BusinessActivityPublisher will find the matching ActivityType and ObjectType defined for this Activity in service-definition.xml and publish the Activity to the ActivityStreaming Service asynchronously. Details of the API on this class are shown in Example 15-10.

BusinessActivity

This is an abstract class that is used to represent an Activity corresponding to a business event. BusinessActivityPublisher:publishActivity() takes an instance of this class as a parameter. You would implement an instance of this class to encapsulate the details of the Activity corresponding to the non entity object-based model changes and invoke the publishActivity API with this as a parameter. BusinessActivityPublisher will find the matching ActivityType and ObjectType defined for this Activity in the service-definition.xml file and publish the Activity to the ActivityStreaming Service asynchronously. Details of the API on this class are shown in Example 15-11.

Example 15-9 BusinessActivityPublisher.java

/**
 * This class is responsible for publishing business events as WebCenter Portal 
 * Activities tothe ActivityStreaming service. This is a singleton per Java EE 
 * application. An instance of this object is obtained using the getInstance() 
 * method. This classtransforms business events into Activities and publishes them 
 * to Activity Service asynchronously. Resources held by the class are released by 
 * usingrelease() method. In Java EE container, release is done by  
 * Applications Core when the application is undeployed or stopped.
 */
public class BusinessActivityPublisher
{
    /**
     * Returns and instance of BusinessActivityPublisher if one exists or
     * creates a new one.
     * @return
     */
    public synchronized static BusinessActivityPublisher getInstance()
 
    /**
     * Queues the Activity for publishing. The queued activities are published
     * to the Activity Streaming service asynchronously if there is a matching
     * ActivityType defined for the source business event. If no matching
     * ActivityType is found in the service-definition.xml corresponding to the
     * source entity object, the activity is ignored.
     * @param activity
     */
    public void publishActivity(BusinessActivity activity)
 
    /**
     * Should be called during App undeploy to stop the publisher thread.
     * In a Java EE container, this method is called by Applications Core
     * ServletContextListener.
     */
    public void release()

Example 15-10 BusinessActivity.java

/**
   * Name of the ActivityType defined in service-definition that
   * corresponds to serviceId returned by getServiceId().
   * @return Name of the ActivityType
   */
  public String getName()
 
  /**
   * ID of the service-definition containing the metadata for this
   * Activity.
   * @return serviceId
   */
  public abstract String getServiceId();
 
  /**
   * Array of GUIDs for Actors of the Activity.
   * @return array of guids for the Actors.
   */
  public abstract String[] getActors();
 
  /**
   * This api will return  additional service ids
   * @return Array of ServiceIds
   */
  public String[] getAdditionalServiceIds(){ return null};
 
  /**
   * This attr provides a "," separated list of object type
   * names associated with a particular Activity.
   * @return
   */
  protected String getActivityObjectTypeNames(){
   return null;
  } 
 
  **
   * Payload for the Activity. Every attribute that is part of this payload is
   * persisted in WC as custom attribute of the Activity Object so only
   * attributes needed for the Activity Message should be added to the payload
   * to avoid performance overhead.
   * The payload typically  contains:
   * 1. Attribute(s) whose name matches the object-type name attribute in
   *    service-definition. This value is used in generating the object-id
   *    of the object referenced in the Activity Stream message.
   * 2. All the attributes referenced in the Message format using {custom}
   *    token.
   * @return a map containing the attribute names and their values needed to
   * display the Activity Message for this Activity.
   */
  public abstract Map getPayload();

Example 15-11 BusinessActivity.java

/**
  * Name of the ActivityType defined in service-definition that
  * corresponds to serviceId returned by getServiceId().
  * @return Name of the ActivityType
  */
 public String getName()
 
 /**
  * ID of the service-definition containing the metadata for this
  * Activity.
  * @return serviceId
  */
 public abstract String getServiceId();
 
 /**
  * Array of GUIDs for Actors of the Activity.
  * @return array of guids for the Actors.
  */
 public abstract String[] getActors();
 
 /**
  * This api will return  additional service ids
  * @return Array of ServiceIds
  */
 public String[] getAdditionalServiceIds(){ return null};
 
 /**
  * This attr provides a "," separated list of object type
  * names associated with a particular Activity.
  * @return
  */
 protected String getActivityObjectTypeNames(){
  return null;
 } 
 
 **
  * Payload for the Activity. Every attribute that is part of this payload is
  * persisted in WC as custom attribute of the Activity Object so only
  * attributes needed for the Activity Message should be added to the payload
  * to avoid performance overhead.
  * The payload typically  contains:
  * 1. Attribute(s) whose name matches the object-type name attribute in
  *    service-definition. This value is used in generating the object-id
  *    of the object referenced in the Activity Stream message.
  * 2. All the attributes referenced in the Message format using {custom}
  *    token.
  * @return a map containing the attribute names and their values needed to
  * display the Activity Message for this Activity.
  */
 public abstract Map getPayload();

15.5.4 How to Implement Activity Streams

This section provides details about the steps involved in integrating this feature with Oracle Fusion Applications.

15.5.4.1 Defining and Publishing Business Events in JDeveloper

To define a business event, follow these steps:

  1. In the Application Navigator, double-click an entity object.
  2. In the overview editor, click the business events navigation tab.
  3. On the business events page, expand the Event Publication section and click the Edit event publications icon.
  4. In the Edit Event Publications dialog, click New to create a new event.
  5. Double-click the new cell in the Event column, and select the appropriate event.
  6. Double-click the corresponding cell in the Event Point column, and select the appropriate event point action.
  7. You optionally can define conditions for raising the event using the Raise Conditions table.
  8. Click OK.

An event definition in the Entity XML file would look similar to:

<EventDef  Name="OpportunityStatusUpdate">
  <Payload>
    <PayloadItem AttrName="OpptyId"/>
    <PayloadItem AttrName="Status"/>
    <PayloadItem AttrName="Customer.CustomerId"/>
  </Payload>
</EventDef>

15.5.4.2 Overriding isActivityPublishingEnabled() to Enable Activity Publishing

By default, business events are not published as Activities. You should override the isActivityPublishingEnabled() method to enable Activity publishing for an entity object. Table 15-5 shows the details about the APIs exposed in OAEntityImpl that you can override.

Note that, except for the isActivityPublishingEnabled() method, other methods mentioned in Table 15-5 should be avoided in favor of transient attributes specified in Defining Activity Attributes Declaratively.

Table 15-5 Overriding isActivityPublishingEnabled()

Method Return Type Description Optional/Required Corresponding Declarative Transient Attribute (See Defining Activity Attributes Declaratively)

isActivityPublishingEnabled()

boolean

By default the base class implementation returns false. This will enable activity publishing for this entity object.

Required

None

getActivityActorsGUIDs()

String []

This can be overridden to provide an array of GUIDs for the Actors involved with the activity. By default, the framework will use the GUID of the user currently logged in when the business event is raised.

Optional

WCActivityActorGuid1, WCActivityActorGuid2

getActivityStreamServiceId()

String

This returns the service ID to be used to publish the activities for the business events raised for this entity. By default this returns null. When null is returned, the service ID defaults to the full name of the entity object.

Optional

WCActivityServiceId

getAdditionalServiceIds()

String []

Override this API to support publishing multiple Activities in response to a single event. So additional service IDs can be passed for a single activity.

Optional

WCAdditionalActivityServiceId1, WCAdditionalActivityServiceId2

getActivityObjectTypeNames()

String

This API can be overridden to return multiple object type names. Values provided should be a comma-separated list of object type names associated with a particular Activity.

The first object-type in this string will be used to create the custom attributes needed for the primary object. When creating the primary object for an Activity, the object-type of the first object listed in the service-definition.xml file is necessary even though the custom attributes used to construct this object are fetched from a different object-type based on getActivityObjectTypeNames() or WCActivityObjectTypeNames. This is necessary for the follow model to work. The order of the objects in this array can be used to reference the objects in an Activity message format string.

Optional

WCActivityObjectTypeNames

15.5.4.3 Defining Activity Attributes Declaratively

Some of the attributes, such as Actor, Service Ids and Additional Service Ids, can be passed as a part of the payload. The basic process steps are:

  • Define a transient attribute in the entity object.

  • Give a default value to the transient attribute.

  • Include the transient attribute as a part of the payload.

The different transient attributes that can be passed with the payload are shown in Table 15-6.

Table 15-6 Transient Attributes that Can Be Passed with the Payload

Attribute Type Description Sample Value

WCActivityServiceId

String

This attribute's value is used to identify the business object service to be associated with the Activity.

oracle.apps.crmdemo.model.OpportunityEO

WCAdditionalActivityServiceId1

String

This attribute's value is used to identify any additional service that must be associated with the Activity.

Values are similar to those of WCActivityServiceId

WCAdditionalActivityServiceId2

String

This attribute's value is used to identify any additional service that must be associated with the Activity.

Values are similar to those of WCActivityServiceId

WCActivityActorGuid1

String

This attribute's value is used to identify any actor that must be associated with the Activity if the default actor value must be overridden. For instance, in the message it can be accessed as actor[0]

<User_GUID>

WCActivityActorGuid2

String

This attribute's value is used to identify any actor that must be associated with the Activity if the default actor value must be overridden. For instance, in the message it can be accessed as actor[1]

<User_GUID>

WCActivityObjectTypeNames

String

This attribute should provide a comma-separated list of object-type names associated with a particular Activity. Programmatically getActivityObjectTypeNames() API in the entity object's EntityImpl.

The first object- type in this string will be used to create the custom attributes needed for the primary object. When creating the primary object for an Activity, the object type of the first object listed in the service-definition.xml file is necessary even though the custom attributes used to construct this object are fetched from a different object type based on getActivityObjectTypeNames() or WCActivityObjectTypeNames. This is necessary for the follow model to work.

The order of the objects in this array can be used to reference the objects in the Activity message format string.

Emp, Dept

15.5.5 How to Define Activities

Defining Activities requires:

  • Adding the ActivityStream UI task flow

  • Defining Activities in the service-definition.xml file

15.5.5.1 Add the ActivityStream UI Task Flow

To add the ActivityStream task flow:

  1. Ensure your user interface project includes the Oracle WebCenter Portal Activity Streaming Service library in the Libraries and Classpath section in the project properties dialog.
  2. From Resource Catalog > TaskFlows, drag and drop either an "Activity Stream" or "Activity Stream Summary - View" task flow onto the page where you want to display the Activity Stream UI.
  3. Set the taskFlow resourceId parameter to #{securityContext.userName}. This will tie the task flow to the current user at runtime.
  4. For additional details about the Activity Stream task flow, see the chapters in the Working with the People Connections Service partition in the Developing WebCenter Portal Assets and Custom Components with Oracle JDeveloper.

15.5.5.2 Define Activities in the service-definition.xml File

The default location of the service-definition.xml file is under META-INF in the project. For Oracle Fusion Applications, this file will be stored in MDS so you need to put it into a directory that can be added to your Metadata Archive (MAR) file.

The standardized location that all applications should use is:

<app>/<lba>/<product>/<*project*>/*meta/oracle/apps/meta*/<lba>/<product>/service-definition.xml

The name must be unique, such as:

helpPortal/atk/helpPortal/model/meta/oracle/apps/meta/atk/helpPortal/service-definition.xml

To define Activities in the service-definition.xml file, follow these steps.

  1. If necessary, add the directory to the application's MAR profile. To add a MAR, select Application > Application Properties > Deployment. In the dialog that is displayed, select the MAR file and click Edit.

  2. In the Edit dialog, select User Metadata and click Add. Browse to the metadata directory that was just added and click OK.

  3. Set the id attribute on the service-definition element to the entity object name for which you want to define the Activities.

    <service-definition xmlns="http://xmlns.oracle.com/webcenter/framework/service"
                  id="oracle.apps.crmdemo.model.OpportunityEO"
                  version="11.1.1.0.0">
    
  4. Define an activity type for every business event in the entity object for which you want to display the Activities in the Activity Stream UI. Ensure the event name of the entity object matches the activity-type name attribute value.

    <activity-types>
      <generic-activity-category name="UPDATE">
        <activity-type name="OpportunityStatusUpdate"
                       displayName="Opportunity Status Update"
                       description="Opportunity Status Update"
                       messageFormatKey="OPPTY_STATUS_UPDATED"
                       iconURL=""
                       defaultPermissionLevel="SHARED"/>
      </generic-activity-category>
    </activity-types>
    
  5. Set message format strings.

    Each activity-type should have a message format defined. The message format string can be translated and is stored in a Resource Bundle or an XLIFF bundle. Oracle Fusion Applications uses XLIFF bundles to store the Activity message format strings. The activity-type element in the service-definition.xml file has messageFormatKey attributes that are used to refer to the format strings in the XLIFF bundle.

    Activity Stream supports only Java Resource Bundles. The Common String Repository is used for the message format strings.

    These attributes are supported on the activity-type element:

    • messageFormatKey - Used on the Activity Stream full view task flow.

    • summaryByListMessageFormatKey - Used in the summary view Activity Stream task flow.

    • summaryByCountMessageFormatKey - Used in the summary view task flow.

    messageFormatKey

    The value of this attribute points to the key defined in the Resource Bundle. These tokens are supported in the message format string.

    • {actor[0]}: Replaced by the display name of the user who triggered the event.

    • {object[0]}: Replaced by the value of the attribute in the event payload whose attribute name matches the object type name.

    • {custom[attr1].value}: Replaced by the value of the attr1 attribute in the event's payload.

    The following sample uses the Java Resource Bundle class:

    <resource-bundle-class>oracle.apps.crm.OpportunityResourceBundle</resource-bundle-class>
     <activity-types>
       <generic-activity-category name="OPPTYUPDATE">
         <activity-type name="OpptyStatusUpdate"
                        displayName="OPPTY_UPDATE"
                        description="OPPTY_UPDATE_DESCRIPTION"
                        messageFormatKey="OPPTY_STATUS_UPDATED"
                        defaultPermissionLevel="SHARED"/>
        </generic-activity-category>
     </activity-types>
    

    In OpportunityResourceBundle, the OPPTY_STATUS_UPDATED key is defined as:

    {"OPPTY_STATUS_UPDATED", "{actor\[0\]} updated {object\[0\]} status to {custom\['status'\].value}"}
    

    summaryByListMessageFormatKey and summaryByCountMessageFormatKey

    These attributes are used only when the Activity Stream summarized view task flow is used. The summarized view task flow is used on the portrait page in My Activities and Network Activities mini cards. In summarized view, the Activity messages are summarized or grouped based on an Activity Type. For instance, if multiple Activities of the same type are published, they are combined and displayed as a single Activity message. Within the group of Activities of the same type, the following algorithm is used to generate summarized messages:

    1. Summarize activities by finding a common object referenced in the Activity.

    2. Summarize or aggregate the Actors either by listing them if there are three or fewer, or by counting them if there more than three.

    3. For remaining activities, summarize by finding a common Actor. Summarize or aggregate the objects either by listing them if there are three or fewer, or by counting them if there are more than three.

    For example, note the following activities:

    1. James updated Project Alpha tasks.

    2. Viju updated Project Alpha tasks.

    3. Ling updated Project Alpha tasks.

    4. Monty updated Oppty 200 laptops status

    5. Monty updated Oppty Solaris workstations status

    6. Monty updated Oppty 2 DB machines status.

    In the summarized view, the Activities are summarized as follows:

    • Activities a-c: James, Viju, Ling updated Project Alpha tasks.

    • Activities d-f: Monty updated 200 laptops, Solaris workstations, 2 DB machines opportunities status.

    Example 15-12 shows sample format strings for the preceding scenario.

    In the OpportunityResourceBundle, the keys are defined as:

    {"OPPTY_STATUS_UPDATED_SUMMARY_LIST", "{actor\[0\]} updated status for opportunity {object\[0\]} }"}
    {"OPPTY_STATUS_UPDATED_SUMMARY_CNT", "{actor\[0\]} updated {object\[0\].count} opportunities status"}
    
  6. Define an object type for the entity object that is the source of the events. Even though Oracle WebCenter Portal supports multiple object types, for Oracle Fusion Applications, only one object type that corresponds to the source of the events is supported. The value of the name attribute should match the name of the attribute in the business event's payload. This attribute's value will be used as the display name of the object when displayed in the Activity message. The primary key of the event source will be used as the object ID.

    <object-types>
       <object-type name="OpptyId"
                    displayName="Opportunity Object"
                    description="Opportunity Object"
                    iconURL="">
       </object-type>
    </object-types>
    
  7. The ObjectType custom attributes can be used to provide additional metadata for handling business object references in Activity Stream messages. The custom attributes shown in Table 15-7 will be used.

    Table 15-7 ObjectType Custom Attributes

    Name Description

    service-ref-id

    ID of the service-definition of a business object referenced in the Activity message of the current business object. This is used when an Activity message contains multiple object references and is used to reference the serviceId of some other business object.

    object-id-attr

    The name of the attribute in the business event payload that is necessary as the object-id for an Activity object. Typically this corresponds to the primary key attribute of a business object. If this attribute is not specified, the object-type element's name attribute is used as the default.

    display-name-attr

    The name of the attribute in the business event payload that is necessary as the display name of the Activity object. This attribute's value will be used to replace the {object} token in the Activity's message format.

    <object-type>
      <custom-attributes>
        <custom-attribute name="service-ref-id" defaultValue="oracle.apps.crm.model.OpptyEO"/>
        <custom-attribute name="object-id-attr" defaultValue="opptyId"/>
        <custom-attribute name="display-name-attr" defaultValue="opptyName"/>
      </custom-attributes>
    </object-type>
    
  8. Define resource view handler parameters to allow custom navigation for links rendered in the Activity message. Navigation from the Actor link in the Activity message navigates to the user's portrait page. Custom navigation to the business object workarea is supported. Important fields include:

    • taskFlowId: The task flow where you want to go.

    • resourceParamList: The list of parameters that you want to pass to the task flow. For example, if a business object task flow takes the opptyId parameter, in resourceParamList you should specify "opptyId". If multiple parameters are required, the parameters should be separated by a semi-colon (;), for example "opptyId;opptyType". When the hyper link is clicked, parameters opptyId="oppty id value" and opptyType="type value" will be passed as input parameters to the task flow.

      <resource-view taskFlowId="/WEB-INF/OpportunityTF.xml#OpportunityTF">
        <parameters>
          <parameter name="viewId" value="Opportunity"/>
          <parameter name="webApp" value="CRMApp"/>
          <parameter name="pageParametersList" value=""/>
          <parameter name="taskParametersList" value=""/>
          <parameter name="resourceParamList" value="opptyId"/>
        </parameters>
      </resource-view>
      

      Custom navigation is handled by the Applications Core ResourceViewHandler registered in the adf-config.xml file. You must add this entry to all adf-config.xml files:

      <wpsC:adf-service-config xmlns:wpsC="http://xmlns.oracle.com/webcenter/framework/service">
          <resource-handler class="oracle.apps.fnd.applcore.tags.handler.FndResourceActionViewHandler"/>
      </wpsC:adf-service-config>
      

Example 15-12 Sample Format Strings for a Summarized View

<resource-bundle-class>oracle.apps.crm.OpportunityResourceBundle</resource-bundle-class>
 <activity-types>
   <generic-activity-category name="OPPTYUPDATE">
     <activity-type name="OpptyStatusUpdate"
               displayName="OPPTY_UPDATE"
               description="OPPTY_UPDATE_DESCRIPTION"
               messageFormatKey="OPPTY_STATUS_UPDATED"
               summaryByListMessageFormatKey="OPPTY_STATUS_UPDATED_SUMMARY_LIST"
               summaryByCountMessageFormatKey="OPPTY_STATUS_UPDATED_SUMMARY_CNT"
               defaultPermissionLevel="SHARED"/>
    </generic-activity-category>
 </activity-types>

15.5.6 How to Implement Comments and Likes

Commenting allows users to comment on objects that are created or published by various users on the site, and engage in discussions revolving around those objects using replies to comments and comments upon comments. This feature in Activity Stream allows users to comment on a specific Activity related to a object.

The Likes feature allows users to express their liking for any object in the system to which they have access. This feature is exposed in message boards, Activity Streams, doclib and replies on topics in discussion forums. In Activity Stream, this feature allows users to indicate if they like a particular Activity.

To enable Comments and Likes for a service, add these Activity Types to the service-definitio.xml file:

<activity-type name="postComment"
               messageFormatKey="ACTIVITY_COMMENT_STATUS"/>
<activity-type name="expressLike"
               messageFormatKey="ACTIVITY_LIKE_STATUS"/>

Ensure the activity-type names are as shown. The messageFormatKey values refer to the ResourceBundle keys that provide strings displayed for "comments" and "likes" links displayed in the Activity message.

15.5.7 How to Implement Follow for an Object

Users will be able to see Activity messages belonging to the business objects they are following. Users should either explicitly follow a business object, or you should provide a way for users to follow certain business objects implicitly. A business object can be followed for a user by using the Oracle WebCenter Portal Follow API. A sample implementation of the Follow model is shown in Example 15-13.

Example 15-13 Sample Implementation of the Follow Model

public void follow() {
   System.out.println("Follow method invoked!!!");
   try
   {
   OAViewObjectImpl vo = getCaseList1();
   Row row = vo.getCurrentRow();
   Object id = row.getAttribute("Id");
   System.out.println("Case Id in follow : " + id);
 
   ActivityStreamingService asService = ActivityStreamingServiceFactory
     .getInstance().getActivityStreamingService();
   FollowManager followManager = asService.getFollowManager();
   String serviceID = "oracle.apps.fnd.applcore.crmdemo.model.business.CasesEO";
   String userGUID = ApplSessionUtil.getSession().getUserGuid();
   ActivityActor actor = asService.createActor(userGUID);
   String objectTypeName = "Id";
   ServiceObjectType objectType = asService.findObjectType(serviceID, objectTypeName);
   ActivityObject followedObject = asService.createObject(id.toString(),
                                    objectType, "");
   System.out.println("Calling Follow for Case : " + id);
 
   followedObject.setServiceID(serviceID);
   followManager.followObject(actor, followedObject);
   }
   catch(ActivityException ae) {
     ae.printStackTrace();
     System.out.println("Case follow failed");
   }
}

15.5.7.1 Defining the Service Category

The Follow model is enforced for Activity messages when the category-id of a service contains "business" in its name. A sample service-category-definition and its reference in service-definition are provided in Example 15-14 and Example 15-15.

Note that the ID in the service-category-definition file matches the category-id in the service-definition.xml file and it contains "business".

Example 15-14 Sample service-category-definition.xml File

<service-category-definition xmlns="http://xmlns.oracle.com/webcenter">
 <category id="oracle.apps.fnd.applcore.crmdemo.model.business.CasesEO"
           resourceBundle="oracle.apps.fnd.applcore.crmdemo.BusinessActivityServiceResourceBundle"
           titleKey="CASE_SERVICE_CATEGORY"
           icon="/a/s/g.gif"/>
</service-category-definition>

Example 15-15 Sample service-category-definition Reference

<category-id>oracle.apps.fnd.applcore.crmdemo.model.business.CasesEO</category-id>

15.5.7.2 Adding ActivityTypes for Follow and Unfollow

The Activity types shown in Example 15-16 should be added to the service-definitions of all services that use the Follow model. These Activity types are used to construct the message published when an object belonging to the service is Followed or Unfollowed.

Example 15-16 Adding ActivityTypes for Follow and Unfollow

<activity-type name="followObject"
   displayName="Follow Object"
   messageFormatKey="ACTIVITY_FOLLOW_OBJECT_MSG"
   description="Follow Object">
</activity-type>
 
<activity-type name="unfollowObject"
   displayName="Unfollow Object"
   messageFormatKey="ACTIVITY_UNFOLLOW_OBJECT_MSG"
   description="Unfollow Object">
</activity-type>

15.5.8 How to Render Contextual Actions in Activity Streams

Contextual Actions are rendered for business objects or other resources referenced in Activity Stream messages when contextInfoPopupId is configured in the service-definition.xml file of the business object or resource. The Activity Stream starts an Oracle ADF popup using the popup ID from the service-definition.xml file. The contextInfoPopupId should provide the absolute ID of the popup used for the Contextual Action. A popup with the specified ID should exist in the pages where the Activity Stream is used. This is a requirement for all pages where Contextual Actions-enabled objects are rendered. The Activity Stream will make the serviceId, resourceId, and resourceType properties available to the started popup. The popup should process these parameters and convert them to Contextual Actions-specific parameters and make them available to the Contextual Actions task flow or another component.

This element, which is the direct child of the service-definition element, is used to configure the Contextual Actions popup ID in the service-definition.xml file.

<contextInfoPopupId>:pt1:r1:casePopup</contextInfoPopupId>

The popup sample shown in Example 15-17 uses the serviceId, resourceId, and resourceType properties from the Activity Stream that are made available through the launch variable, and makes them available to the popup.

Example 15-17 Sample Popup for ActivityStream

<af:popup id="casePopup" contentDelivery="lazyUncached"
    eventContext="launcher" launcherVar="source"
    clientComponent="true">
  <af:noteWindow id="nw" >
  <af:panelFormLayout id="pflTst">
  <af:inputText id="itSID"
    label="Service ID"
    value="#{pageFlowScope.serviceId}"
    readOnly="true"/>
  <af:inputText id="itRID"
    label="Resource ID"
    value="#{pageFlowScope.resourceId}"
    readOnly="true"/>
  <af:inputText id="itRType"
    label="Resource Type"
    value="#{pageFlowScope.resourceType}"
    readOnly="true"/>
 </af:panelFormLayout>
</af:noteWindow>
<af:setPropertyListener from="#{source.attributes.serviceId}"
                         to="#{pageFlowScope.serviceId}"
                         type="popupFetch"/>
<af:setPropertyListener from="#{source.attributes.resourceId}"
                         to="#{pageFlowScope.resourceId}"
                         type="popupFetch"/>
<af:setPropertyListener from="#{source.attributes.resourceType}"
                        to="#{pageFlowScope.resourceType}"
                        type="popupFetch"/>
</af:popup>

15.6 Implementing the Oracle Fusion Applications Search Results UI

The Oracle Fusion Applications Search Results UI is an implementation of the Oracle Enterprise Crawl and Search Framework (ECSF) within the Applications Core UIShell and SimpleUIShell (FUSE+) page templates. It allows all users on any page to perform searches in a consistent manner.

The minimum requirement is to implement and run a UI Shell template page. A page using the UI Shell template automatically will contain the search components in the Global Area and can be activated when running the page.

Where you have implemented ECSF for your product, you must ensure that you have followed all of the instructions in Set Up Your Development Environment, Getting Started with Oracle Enterprise Crawl and Search Framework, and Creating Searchable Objects and in particular:

  • "How to Create a Database Connection" in Set Up Your Development Environment. The database connection must be defined inside the project using the UI Shell template. The Oracle Fusion Applications Search Results UI functionality uses the database connection SearchDB defined in this section to connect to the Oracle database to query the results.

  • Creating Searchable Objects to make your view objects searchable. ECSF uses these search-enabled objects in the construction of the result set.

If you have implemented ECSF and defined the SearchDB connection, the saved and recent searches are saved to the Oracle database and are persisted across sessions.

Security on search is implemented by a combination of Functional Security over search categories at the ECSF layer, and Data Security (limiting search results to only those items to which the user has authorized access) using Applications Core data security rules on the searchable view objects.

15.6.1 How to Disable Oracle Fusion Applications Search

There are occasions when you will want to disable Oracle Fusion Applications Search for an application, such as for public (unauthenticated) pages. There are four ways to disable the function:

  • The preferred method is to set the profile option Fusion Apps Search Enabled to 'N', either at site level or user level.

  • Setup switch using a JVM system property.

    -DFUSION_APPS_SEARCH_ENGINE_AVAILABLE=N
    
  • Use Customization by setting rendered to false on the panelGroupLayout with id "_UISGlobalSearch" (the panel containing the Oracle Fusion Applications Search fields) in the UI Shell main area. Note that by customizing the fields from the current page, you are not disabling search; the Expression Language bindings on the fields are still evaluated and if the user opens a non-customized page, Oracle Fusion Applications Search will be available.

  • Remove the ECSF libraries (oracle.ecsf shared lib). Oracle Fusion Applications Search will detect the missing dependency and disable itself for all pages in the current user session, even if the user opens a web application that does have the ECSF libraries available.

15.6.2 How to Use Basic Search

From the main page of the project in the Global Area, click the Search icon to display the Search dialog:

Figure 15-21 Initial Search Dialog

Initial Search Dialog

When you click the search icon, a popup will show suggestions. By default, these will contain any Recent Searches, Saved Searches and Recent Items the user has performed. As the user types, these suggestions will narrow down, and other suggestions, such as previous search terms, will be shown.

The Groups of suggestions shown to the user are determined by the search configuration in effect (see Using Setup Task Flows in ), and personalizations made by the user.

By clicking the gear icon, the user can access these personalizations and can select/deselect suggestion groups, reorder them, and so on.

When you click OK, the selections are saved as MDS personalizations that will be stored across login sessions for the user.

Hint: To reset the personalizations, hold down the Ctrl key and click the gear icon. Your previous selections will be deleted and you will see the default configuration the next time you log in.

Figure 15-22 Modifying Suggestion Groups

Modifying Suggestion Groups

When you click a Recent Searches link or a Saved Searches link, the search will be run.

When you click a Recent Items link, the task flow will be re-opened. As you type in the search field, the characters typed will narrow down the suggestions shown for the Recent Items, Recent Searches and Saved Searches. In addition, suggestions for previous searches will be shown.

Recent Searches

When you start to type in the search field, the Recent Searches list will refresh and narrow down based on the substring you type.

Keywords entered by the user serve as the name of the recent search.

Recent searches are implemented using the ECSF recent searches feature. See About Managing Recent Searches.

A recent search is uniquely identified by its filters, such as search term, categories, and facet selections.

A search will be added to the front of the recent search list when performed. If it already exists in the list, it will be moved from its current place in the list to the front.

Search Term

This is a text field for the values on which to search. The field can hold a maximum of 2048 characters.

The term is searched for in any of the crawled data, which includes the title, fixed and variable content, attached documents, and tags. So if the search term is foo, the search returns any data containing the word foo.

Click the Play button or press Enter to initiate the search.

Click the Show Filters link to view and change the filters that will be used for the search.

Figure 15-23 Category Filters Dialog

Category Filters Dialog

Once a search has been performed, the Search terms field and the search results are displayed, as shown in Figure 15-24.

Figure 15-24 Basic Search Functions in UI Shell

Described in the surrounding text.

Sort

If multiple results are displayed, the Sort icon to the right of the More than X Results heading becomes active. Click the icon to display the Sort By dialog.

Figure 15-25 Sort By Dialog

Explained in surrounding text.

This function requires no developer implementation; it is built in.

Users can sort results in the results table. The sort may be done in ascending or descending order, and may be switched using a toggle button.

The sort will be available in two forms, depending on the search parameters:

  • Multiple categories or a single category

    The sort can be based only on Relevance (an implicit universal attribute) and LastModifiedDate (a universal attribute).

  • A Single Searchable Object in a category

    The sort also can be based on Relevance and LastModifiedDate, and any stored attributes defined on the searchable object.

The Search result will be expanded in the background from the initial 10 results returned with the query, to 100 results returned by a background search started in a separate thread as soon as the 10 results are successfully returned. This is to give a reasonable result to sort. The sort UI will show a spinner and will be disabled until this is finished, and the UI will poll for completion every two seconds and enable those fields. This polling will stop when the background search is complete, or after a fixed number of polls (to stop infinite polling in case of error).

The default Sort is Relevance descending, which is the way that records are returned by SES.

The sort is done in memory using a standard Java Collections.sort function, and a comparator that takes into account the date type of the attribute (Date/Number/String) and direction.

15.6.2.1 Using the Search Filters

Click Show Filters to display them on the left side of the display. They are sized to make them easy to use on touch-screen appliances.

Last Updated Date and Categories are default filters.

Figure 15-26 Search Filters Buttons

Search Filters Buttons

Last Updated Date

Click Last Updated Date to display the selections panel, similar to Figure 15-27.

Figure 15-27 Last Updated Date Search Filter

Described in the surrounding text.

The valid values are:

  • All

    Clears the current filter and displays results for all available dates.

  • Today

    LastModifiedDate equals "todays date."

  • This Week

    LastModifiedDate >= "last Sunday" AND LastModifiedDate <= "this Saturday"

  • This Month

    LastModifiedDate >= "first day of month" AND LastModifiedDate <= "last day of month"

  • This Year

    LastModifiedDate >= "first day of year" AND LastModifiedDate <= "last day of year"

  • Custom Date Range

    As appropriate for the range. A date picker will be displayed. The dates in the custom date range default to the last year (last 365 days).

Categories

Note:

Categories are not set up at design time by developers. They should be set up either by customers or seeded by teams. Categories are created and stored in ECSF schema, and ECSF provides an API to get a list of categories to the UI for a given user. See How to Control the Look and Feel of Search.

When you click Categories, a list, similar to that shown in Figure 15-28, is displayed:

Figure 15-28 Search Categories Field Expanded

Described in the surrounding text.

The user can select from the list of Categories. Unchecking the All category unchecks all of the categories. The subset of selected categories will be displayed in the entry area of the dropdown list as a concatenated list separated by commas.

Figure 15-29 Individually-selected Categories List

Individually-selected Categories List

Alternate Word List

Oracle Secure Enterprise Search (SES) will show alternate words to the user when they do a search as suggestions to frequent typos, or better used terms. This list is based on statically defined lists stored in SES.

Oracle Fusion Applications Search uses the ECSF APIs to show the alternate words to the user. Clicking the alternate word does a new search using the selected alternate word as the new keyword.

Saved Searches

Click Save to open a list of saved searches.

Figure 15-30 Saved Searches List

Described in the surrounding text.
  • To create a new saved search that uses the last search string seen in the Search window as the search criteria, enter a name in the Name field and click OK. Make sure you have not selected an existing search.

  • To rename a saved search, select it, enter a new name in the Name field, and click OK.

  • To delete a saved search, select it and click Delete.

15.6.2.2 Search Results

After clicking Search, a modal dialog will display the results of the search. Hovering over the main link will show the last crawled date. Figure 15-31 shows typical results.

Note:

If a search application, such as Finance or HCM, does not respond to the search request within a predetermined period, the search results will be displayed but there will be a notice that one or more applications did not respond.

Figure 15-31 Search Results Example

Described in the surrounding text.

The Search Results display consists of:

  • A repetition of the fields displayed in the Global Area.

  • A set of filter tiles: Selected filter values will be applied to the search results.

    A category is a group of related objects. Examples include any Oracle Fusion business object, and Oracle WebCenter Portal objects such as wikis and blogs.

    A Searchable Object is the second level. A searchable object is the view object.

    If a category has only one Searchable Object, it is not shown as a filter because there is no action a user can take on it; it is automatically selected. Figure 15-31 shows an "Emp Cat" that has one searchable Object (not shown) that has three facet filters.

    Facets are formed by the Lists of Values defined on an attribute in the Searchable Object. There may be many facets for a Searchable Object, and the facets may be hierarchical, such as is the case in Figure 15-31, where a State facet contains a County facet, which contains a City facet. Only the name of the highest level facet in the hierarchy is shown.

  • A Results section. The search results use the keywords in the search field with the filters applied.

    Each result found under a category provides a navigation link back to the record.

    The format of a search result is:

    • A line containing the default or primary action for the result. This may include an icon if it is defined on the search action. This line will be seen for all search results. Clicking the link navigates the user to the primary destination for the result.

    • A line containing the fixed content of the result. If any search terms match in this text, they are shown in bold. The Fixed Content is derived from the Search Properties Title field as defined in ECSF.

    • A line containing the variable content of the result. If any search terms match in this text, they are shown in bold. This text will wrap if it overflows the width of the line. The Fixed Content is derived from the Search Properties Body field as defined in ECSF.

    • A line of tags attached to this result of the form "Tags: tag1 tag2 ...". If there are no tags, this line is not shown.

    • A line of other actions in alphabetic order. Other actions are similar to the default action in that they may consist of an icon followed by a link. Clicking the link navigates the user to subsidiary destinations for the result. For example, the primary action for a result on a purchase order search may be to open the found purchase order. Other actions may include approval or rejection of the purchase order.

    • A line of attachments for this result of the form "Attachments: attach1 attach2 ...". Each attachment may consist of an icon followed by the attachment name. A search may match the search term within the attachment. Clicking an attachment will download and open the attachment. If there are no attachments, this line is not shown.

Result Counts Do Not Add Up

It is possible when viewing the search results, and narrowing your selections using the facet tree, to see counts against the nodes that do not add up. For example, a search on Glasses might return 16. Then if you filter the result by color, you may find Blue (5) and Red (10), which do not add up to 16. This count is the Oracle Secure Enterprise Search (SES) Approximate count based on heuristics, and not an exact count. To make the count exact, start SES and select Global Settings > Query Configuration, and click the Exact count radio button, as shown in Figure 15-32. Note that SES warns against this for performance reasons. See the Oracle Secure Enterprise Search Administration Online Help.

Figure 15-32 Setting the Exact Hit Count in SES

Described in the surrounding text.

15.6.3 How to Control the Look and Feel of Search

15.6.3.1 Using the Setup Task Flows

Two setup task flows are available for developers and customers to use in extending the Global Search metadata.

They will be available in the following Applications Core Libraries.

Table 15-8 Exported JAR Files

JAR Level Library

GlobalSearch-Model.jar

Model

Applications Core (oracle.applcore.model.ear)

GlobalSearch-View.jar

ViewController

Applications Core (Setup UI) (oracle.applcore.setupui.war)

The created task flows are:

Table 15-9 Created Task Flows

Task Flow Name Task Flow Id Description

Manage Global Search Configurations

/WEB-INF/oracle/apps/fnd/applcore/globalSearch/
uiPublic/GlobalSearchManageConfigurationsTF.xml#
GlobalSearchManageConfigurations

Set up and manage the options passed to Global Search on a per View basis.

Manage Global Search Suggestion Groups

/WEB-INF/oracle/apps/fnd/applcore/globalSearch/
uiPublic/GlobalSearchManageGroupsTF.xml#
GlobalSearchManageGroups

Set up and manage Search Suggest Groups.

15.6.3.2 Using Setup Task Flows in Fusion Applications

To access the setup flows in Fusion Applications:

  1. Select Setup and Maintenance from the Settings and Actions dropdown on the top of the page. See Using the Settings and Actions Menu.
  2. On the Overview page, select the All Tasks tab.
  3. Set the Search dropdown to "Task Lists and Tasks."
  4. Enter "global search" in the Name field and click Search.

    The Application Global Search Configuration and Manage Suggestion Groups task flows are available under Define Global Search:

    Figure 15-33 Searching for Define Global Search in Settings and Actions

    Explained in surrounding text

    Select the task flow you want to manage and click the Go to Task icon. The details are described in Setting Up a Global Search Configuration.

15.6.3.3 How a Configuration Lookup Is Processed

The configuration is loaded for the page each time it is loaded.

The algorithm is as follows:

  • Determine the viewId and application for the loading page.

  • Search for a configuration of type Page that matches the current viewId (unSEEDED, then SEEDED). If found, use it.

  • Search for a configuration of type Application that matches the current application (unSEEDED, then SEEDED). If found, use it.

  • Load the default.

In all cases, "like" searches are done, so SQL wildcards can be used and are recommended. For Application, these are required at the end because the application name often comes back with a version number, such as "HomePageApp(2.0)", so use an expression such as "HomePageApp%"

In all cases, if there is a customer (non SEEDED) configuration, it will be loaded before the SEEDed version.

15.6.3.4 Set Up a Global Search Configuration

As a developer there is no need to set up and ship a configuration to control the look and feel of Global Search. There is a default configuration that ships and Global Search will behave exactly how it always has. However, if you wish to change how Global Search appears to your customers, you can make and ship your own configuration. How this affects the search UI is described in this section.

Once you have clicked the Go to Task icon, described above, this dialog is opened:

Figure 15-34 Manage Global Search Configurations

Explained in surrounding text

From 1 (Short Name) to 11 (Last Updated Date), the fields are:

  • (1) Short Name: The short name of the configuration. This is not shown in the Global Search UI.

  • (2) Display Name: The display name of the Configuration. This is not shown in the Global Search UI. This value is translated.

  • (3) Description: The Description of the Configuration. This is not shown in the Global Search UI. This value is translated.

  • (4) Default: Is this the default configuration. There can be two Default configurations - a SEEDED configuration supplied by applcore that is the default configuration of Global Search. It cannot be changed or edited (the UI will prevent this), and a single default designated by the customer. A customer default will be loaded before the SEEDED default.

    No Oracle group should create a default group and ship it as SEED data.

    This is not shown in the Global Search UI.

  • (5) Enabled: Is the group enabled. Only enabled groups can be used. This is not shown in the Global Search UI.

  • (6) Product Family: The product family of the configuration. A configuration can only configure search groups in the Common product family (shipped by applcore and available on every pillar) and the same Product Family. This ensures that the Suggestion Group class can be loaded at runtime.

    This is not shown in the Global Search UI.

  • (7) Module: The module of the Configuration. This is used by the SEED data extraction process to distinguish between different groups' SEED data.

    This is not shown in the Global Search UI.

  • (8) Created By : Row Who. This is not shown in the Global Search UI. A row that has Created By = SEED_DATA_FROM_APPLICATION is considered SEED data.

  • (9) Creation Date: Row Who. This is not shown in the Global Search UI.

  • (10) Last Updated By: Row Who. This is not shown in the Global Search UI.

  • (11) Last Updated Date: Row Who. This is not shown in the Global Search UI.

15.6.3.5 Edit Global Search Configuration

To edit a global search configuration, follow these steps:

  1. Select the configuration, as shown in Figure 15-34, and click the Edit icon.

    Note: SEEDED configurations cannot be edited. In particular, the SEEDED DEFAULT configuration can never be edited by developers or end users.

    This dialog, with the Autosuggest tab selected, is shown:

    Figure 15-35 Edit Global Search Autosuggest Configuration

    Explained in surrounding text.

    The fields in the top portion are described with Figure 15-34.

    From 12 (Enable personalization ...) to 19 (Maximum Number ...), the fields are:

    • (12) Enable personalization of search groups: Is the tab of Suggest groups seen in the search field popup behind the gear icon?

      If this and "Enable personalization of search categories" are no set, the gear icon is not shown.

    • (13) Suggestion Groups: The set of Suggestion groups to show. This is an initial developer design decision and determines the groups the user sees in the search popup. The user then can select a subset of these and this selection will be remembered in MDS. Shuttle the groups across that you want to show and override the default enabled state using the picker.

    • (14) Show Suggestion Group Headings: Whether to show the suggestion group header. The header includes an icon (if available) and the heading text.

      Be aware that turning headings off may run suggestions from different groups together making it hard to distinguish them.

    • (15) Show Icons: Should the suggestions show icons? If there is no icon available, or this is set to no, a bullet is shown.

    • (16) No Suggestions Message: Message to show if there are no matching suggestions. If there are no matching suggestions, the suggestion popup will not be shown.

    • (17) Show Top Suggestions: Whether to show top suggestions. Top suggestions are those shown in the suggestion list before the user has entered the "Minimum Characters for Autosuggest" and typically contains a static list of highly used suggestions as desired by the suggestion group author.

    • (18) Minimum Characters for Autosuggest: The minimum number of characters in the search field before search suggest switches from showing top suggestions to suggestions specific to the typed values.

    • (19) Maximum Number of Suggestions : The total maximum number of suggestions shown in the suggest list, spread evenly across all groups.

  2. When finished editing the Autosuggest features, click the Search Field tab to display these settings:

    Figure 15-36 Edit Global Search Search Field Configuration

    Explained in surrounding text.

    From 20 (Minimum Number ...) to 24 (Placeholder), the fields are:

    • (20) Minimum Number of Characters: The Minimum number of characters required for searching. By default this is 1. This will affect the error message "You must enter keywords to search", "You must enter x character(s) to search."

    • (21) Maximum Number of Characters: The maximum number of characters the search field will allow. This sets the underlying af:inputText columns attribute.

    • (22) Automatically Clear Field Value after Search: Is the search field cleared after the search. This is a convenience allowing the user to enter new text without having to clear or edit the previous search terms.

    • (23) Label: The Label of the Search field. To have no label, leave this value blank. For accessibility purposes, the hidden label value will be "Search:"

    • (24) Placeholder: The placeholder or shadow text of the Search field. To have no placeholder, leave this value blank.

  3. When finished editing the Search Field features, click the Search Results tab to display these settings:

    Figure 15-37 Edit Search Results Configuration

    Explained in surrounding text.

    From 25 (Enable saved searches) to 36 (Show Icons), the fields are:

    • (25) Enable saved searches: Whether the user can save a search. The Save button will be hidden.

    • (26) Enable recent searches: Whether recent searches are saved automatically as the user runs and filters searches.

    • (27) Filter Display Style: Whether the filters (Category filters only) are shown inline or using a popup.

    • (28) Enable personalization of search categories: Is the tab of Search Categories seen in the search field popup behind the gear icon?

      If this and "Enable personalization of suggest groups" are not set, the gear icon is not shown.

    • (29) Show subcategories in the search results: Should Searchable Objects, also called subcategories, be shown? If they are not shown, "Show Facets" is also false.

    • (30) Show Facets: Should facets for the selected subcategory be shown?

    • (31) Show hit counts: Should hit counts in the facets be shown?

    • (32) Enable clear all filters: Should the Clear Filters button be shown?

    • (33) Application: The application to which to restrict the search categories. This is optional and will restrict the categories shuttle to only the categories from that application. This drop box is populated with the unique applications with which all categories are registered in the ECSF database.

    • (34) Categories: The set of categories to show. This is an initial developer design decision and determines the categories the user sees in the search popup. The user then can select a subset of these and this selection will be remembered in MDS. To pick all categories (including those added in the future), leave the categories unshuttled.

    • (35) Last Updated Date Filters: The set of Last Updated Date to show. To pick all filters (including those added in the future), leave the filters unshuttled.

    • (36) Show Icons: Whether icons are shown in the results.

  4. When finished editing the Search Results features, click the Pages tab to display these settings:

    Figure 15-38 Edit Global Search Pages Configuration

    Explained in surrounding text.

    The two fields are:

    • (37) View Type: The View Type to which to map this configuration. There are two types of view type: Application and Page.

      You may enter as many rows as needed here.

    • (38) View ID: This value depends on the View Type:

      Application: Enter the application short name with a wildcard at the end, for example HomePageApp%.

      To find the application name:

      1. Open the Setup and Maintenance work area and go to the Manage Taxonomy Hierarchy task.

      2. Expand the Oracle Fusion node, select a row with the Application module type, and click Edit Module.

      3. In the Application Details section, see the Application Short Name column for the value to use.

      Page: Enter the last part of the URL you get when you open that page. For example, enter ExamplePage from the URL.

      http://exampleServer/homePage/faces/ExamplePage

      Note that you cannot enter blind expressions, such as "%" because this effectively short cuts the configuration lookup algorithm. You must enter at least 5 characters that are not wildcard characters.

  5. Click Save and Close or click the Manage Suggest Groups Configuration tab if it is available at the top of the page.

15.6.3.6 Manage Suggestion Groups Configuration

If Suggestion Groups have been implemented (see Create Suggestion Groups, the Manage Suggest Groups Configuration tab will be made available next to the Search Configurations tab, as shown here:

Figure 15-39 Manage Suggestion Groups Configuration

Explained in surrounding text.

To manage a Suggestion Group, follow these steps:

  1. Select a group and click the Edit icon. This configuration dialog is displayed:

    Figure 15-40 Edit Suggestion Group

    Explained in surrounding text.

    From 1 (Short Name) to 16 (Icon), the fields are:

    • (1) Short Name: The Short name of the Suggestion Group. This is not shown in the Global Search UI.

    • (2) Display Name: The Display name of the Suggestion Group. This is not shown in the Global Search UI. This value is translated.

    • (3) Description: The Description of the Suggestion Group. This is not shown in the Global Search UI. This value is translated.

    • (4) Displayed by default: Is this group displayed by default? This determines whether the suggestion group (see item 13 for Figure 15-35) is enabled by default. If it is not, the developer or customer can override the setting to enable the group. This allows a group to be registered and shipped in different configurations with different enabled items.

    • (5) Product Family: The product family of the suggestion group. A Global Search Configuration can only configure search groups in the Common product family (shipped by applcore and available on every pillar) and the same Product Family as the group is declared in. This ensures that the Suggestion Group class can be loaded at runtime by configurations in effect.

      This is not shown in the Global Search UI.

    • (6) Module: The Module of the Configuration. This is used by the SEED data extraction process to distinguish between different groups' SEED data.

      This is not shown in the Global Search UI.

    • (7) Created By: Row Who - this is not shown in the Global Search UI. A row that has "Created By" = SEED_DATA_FROM_APPLICATION is considered SEED data.

    • (8) Creation Date: Row Who. This is not shown in the Global Search UI.

    • (9) Last Updated By: Row Who. This is not shown in the Global Search UI.

    • (10) Last Updated Date: Row Who. This is not shown in the Global Search UI.

    • (11) Data Source: The suggestion group. This can be:

      One of the predefined suggestion groups. These are supplied in an autosuggest list on the field.

      An Expression Language expression that returns an oracle.apps.fnd.applcore.patterns.ui.behaviors.searchSuggest.AbstractSearchSuggestGroup.

      The fully qualified classname of an oracle.apps.fnd.applcore.patterns.ui.behaviors.searchSuggest.AbstractSearchSuggestGroup.

    • (12) Context Code: The Context Code for the suggest group, if it is known at registration time and is to be used in Global Search. Different group use contextCode as an initialization value in different ways. For example the Recent Items group will strip items by the contextCode and objectType. By default, Global Search sets no contextCode and objectType.

    • (13) Object Type: The Object Type for the suggest group; a sub-classification of Context Code, if it is known at registration time and is to be used in Global Search. Different groups use objectType as an initialization value in different ways. For example, the Recent Items group will strip items by the contextCode and objectType. By default, Global Search sets no contextCode and objectType.

    • (14) Heading Visible: Should the heading be shown for this group? Note that heading can be turned off for all groups at the "Global Search Configuration" level (see call out 14 in that section) overriding this value, though if the heading is off here it cannot be turned on at all.

      Be aware that turning headings off may run suggestions from different groups together and make it hard to distinguish them.

    • (15) Text: The Heading Text for the group. If set will override the heading text set in the java code of the Suggestion Group.

    • (16) Icon: The Heading Icon for the group. If set will override the icon set in the java code of the Suggestion Group.

  2. Click Save and Close.

15.6.3.7 Create Suggestion Groups

Developers can create a suggestion group by extending the oracle.apps.fnd.applcore.patterns.ui.behaviors.searchSuggest.AbstractSearchSuggestGroup class.

The simplest way to test at this stage in development is to attach it to an af:inputText field in a simple UI:

<af:inputText label="My Test Suggest Group: " id="simple">
    <fnd:searchSuggestBehavior suggestGroup1="oracle.apps.tla...MySuggestGroup"
         contextCode1="ifNeeded"
         objectType1="ifNeeded"/>
</af:inputText>

To create, register and ship the suggestion group, follow these steps:

  1. Use this boilerplate example to create a suggestion group. You will need to fill in the TODO items.
    import oracle.apps.fnd.applcore.patterns.ui.behaviors.searchSuggest.AbstractSearchSuggestGroup;
    import oracle.apps.fnd.applcore.patterns.ui.behaviors.searchSuggest.SearchSuggestItem;
     
    public class RequiresSubmitSearchSuggestGroup
    extends AbstractSearchSuggestGroup
    {
      /**
       * Unique name of this group.  Must be unique across all groups.
       */
      public static final String NAME = "uniquegroupname";
        // TODO add NAME.
      @Override
      public String getName()
      {
        return NAME;
      }
       
      @Override
      public String getDisplayName()
      {
        // Allow override of Heading in Manage Suggest Groups UI.
        String overRiddenDisplayName = super.getDisplayName();
        if (overRiddenDisplayName != null)
        {
          return overRiddenDisplayName;
        }
        return "Translated Heading";
        // TODO add translated heading.
      }
       
      @Override
      public String getIconURL()
      {
        // Allow override of Icon in Manage Suggest Groups UI.
        String overRiddenIconURL = super.getIconURL();
        if (overRiddenIconURL != null)
        {
          return overRiddenIconURL;
        }
        return "some/path/to/image.png";
        // TODO add icon.
      }
       
      @Override
      public List<SearchSuggestItem> getItems(String searchText,
                                              int maxSuggestedItems)
      {
        List<SearchSuggestItem> ret = new ArrayList<SearchSuggestItem>();
     
        // TODO add suggestion items based on searchText.
        // This call MUST return as fast as possible.  
        ret.add(...);   
     
        return ret;
      }
     
      @Override
      public List<SearchSuggestItem> getTopSuggestions(String searchText,
                                                       int maxSuggestedItems)
      {
        List<SearchSuggestItem> ret = new ArrayList<SearchSuggestItem>();
     
        // TODO add top items.
        // This call MUST return as fast as possible.
        ret.add(...);
         
        return ret;
      }
        
      @Override
      public void init(Map<String, Object> attributes)
      {
        // TODO: Look-up suggestions to use in getItems / getTopSuggestions if needed to allow fast calls of getItems / getTopSuggestions.
        // If they need to be looked up during those calls, you MUST ensure the lookup is fast as it will block the suggest field.
      }
     
      @Override
      public boolean requiresSubmit()
      {
        // TODO: true if the group value selection needs processSubmit(..) to process the selection, false if the suggestions are keywords
        return true;
      }
     
      @Override
      public boolean placeSelectedItemInField()
      {
         // TODO: true to place the selected item in the search field, false otherwise.
         return true;
      }
     
      @Override
      public void processSubmit(String item, Map<String, Object> attributes)
      {
        // TODO: Write code to look-up item and process it according to business rules.
      }
    }
    
  2. Register the group in the setup UI.

    If your suggestion group is to be used in your own UI, just add it to your code.

    If the group is to be used in Global Search, register it in the "Manage Suggestion Group" UI. Use the fully qualified class name as the "Data Source". Use your "Product Family" and "Module".

    Set a "Context Code" and "Object Type" if necessary.

  3. Register a configuration and include your group in it.

    Create a new configuration, or re-use an existing one that you control. The "Product Family" and "Module" should be the same as for the group you have registered.

    Set options as appropriate for your use case. Include the new group and any Common (Applications Core defined) groups you need. Override the display status using the "Enabled" drop box if you wish to change it.

    Map the configuration to your UI pages and applications.

    Do not set the configuration as a default configuration. There should be only one default configuration and that is developed and shipped by applcore. A customer can set a configuration of their own as a default to override the Applications Core default, but it should not be Seeded.

  4. Extract and ship the setup.

    Development teams will:

    • Use the setup UI to define their metadata.

    • Ensure their module is set correctly for their LBA.

    • Use standard Seed data extraction utilities to extract that metadata and ship in their patches.

    Seed data will be extracted separately for:

    • Search Options

    • Search Groups

    Teams will only extract and ship Seed data for the modules they own.

    Use commands similar to these:

    $JDEV_JAVA_HOME/bin/java -cp $JDEV_HOME/jdeveloper/jdev/oaext/lib/oracle.apps.fnd.applseed-rt.jar:$MW_HOME/oracle_common/modules/oracle.xdk_11.1.0/xmlparserv2.jar:$MW_HOME/oracle_common/modules/oracle.odl_11.1.1/ojdl.jar \
    oracle.apps.fnd.applseed.rt.extract.Extract -dburl \
    jdbc:oracle:thin:@slcad942.us.oracle.com:1595:atgucfdv -dbuser fusion \
    -AM oracle.apps.fnd.applcore.search.model.applicationModule.FusionSearchAM \
    -VO FndFusionSuggestGroups \
    -ExtractRootPath $ADE_VIEW_ROOT/atgpf/applcore/db/data
    
    # Search Options.
    $JDEV_JAVA_HOME/bin/java -cp $JDEV_HOME/jdeveloper/jdev/oaext/lib/oracle.apps.fnd.applseed-rt.jar:$MW_HOME/oracle_common/modules/oracle.xdk_11.1.0/xmlparserv2.jar:$MW_HOME/oracle_common/modules/oracle.odl_11.1.1/ojdl.jar \
    oracle.apps.fnd.applseed.rt.extract.Extract -dburl \
    jdbc:oracle:thin:@slcad942.us.oracle.com:1595:atgucfdv -dbuser fusion \
    -AM oracle.apps.fnd.applcore.search.model.applicationModule.FusionSearchAM \
    -VO FndFusionSearchOptions \
    -ExtractRootPath $ADE_VIEW_ROOT/atgpf/applcore/db/data
    

    Use the

    [-PartitionKeyIds <Taxonomy ModuleId values> | -PartitionKeyNames <Taxonomy short name values>] 
    

    parameter to get the Seed data for your product. See Initializing Oracle Fusion Application Data Using the Seed Data Loader for a full description of Seed Data.

15.6.4 Introduction to the Crawled Objects Project

The Crawled Objects Project lets you crawl your Search view objects in Oracle WebLogic Server, and set up Oracle Fusion Applications Search to use those crawled view objects.

The business component objects you will create (specifically the Searchable view object) will contain references to the Oracle Fusion Middleware Extensions for Applications base classes.

Update the ECSF command-line script (runCmdLinScript.sh) to reference the JAR files containing the base classes. Example 15-18 shows the UNIX version; the DOS version is similar.

Example 15-18 Updating ECSF Command-Line Script to Reference Applications Core JAR Files

Run these commands if you are using a Linux operating system:

export APPLCORE_CP=${ORACLE_HOME}/jdeveloper/jdev/oaext/adflib/Common-Model.jar:${ORACLE_HOME}/jdeveloper/jdev/oaext/adflib/Tags-Model.jar
export ADMIN_CLASS=oracle.ecsf.cmdlineadmin.CmdLineAdmin
 
${JAVA_HOME}/java -cp ${ADMIN_CP}:${APPLCORE_CP} ${ADMIN_CLASS} ${CONNECT_INFO}

Run these commands if you are using a Windows operating system:

set APPLCORE_CP=%ORACLE_HOME%\jdeveloper\jdev\oaext\adflib\Common-Model.jar;%ORACLE_HOME%\jdeveloper\jdev\oaext\adflib\Tags-Model.jar
set ADMIN_CLASS=oracle.ecsf.cmdlineadmin.CmdLineAdmin

%JAVA_HOME%\java -cp %ADMIN_CP%;%APPLCORE_CP% %ADMIN_CLASS% %CONNECT_INFO%

15.6.5 How to Implement Tags in Oracle Fusion Applications Search

A view object is available to reference Oracle WebCenter Portal Tags. This view object is available in the ORACLE_HOME/jdeveloper/jdev/oaext/adflib/Tags-Model.jar library JAR file.

You may use this view object using a view link and a predefined Search extension to enable the crawling of Oracle WebCenter Portal Tags, both in initial and incremental (someone has updated the tags) crawls.

Follow these steps:

  1. Create your Searchable view object as usual. Example 15-19 uses a Searchable view object over FND_LOOKUPS_VL in the query.

    The SERVICE_ID specifically identifies your Searchable view object.

    The RESOURCE_ID identifies the specific row for the SERVICE_ID. It is a dot-separated primary key of the entity.

    These two values will be used when setting up tags in your regular UI and must match. For example, if you have a page with a form and a tag button, the Oracle WebCenter Portal tag would be set up as shown in Example 15-20.

    Note:

    Do not forget to mark your key columns and ensure the order is consistent between the view object and the tag:taggingButton.resourceId attribute.

    Figure 15-41 Tags - Searchable View Object Lookup Types

    Described in the surrounding text.

    Figure 15-42 shows the attributes for Searchable view object lookup types.

    Figure 15-42 Tags - Lookup Types

    Described in the surrounding text.
  2. Add a view link to the TagSVO (service view object) linking the Search view object and the Applications Core Tag view object.

    The view link should look similar to Figure 15-43.

    Figure 15-43 View Link Example

    Described in the surrounding text.
  3. Update the Body field to include the tags of the child view object in the relevant position in the string as defined by your management.

    This will be an expression of the form <accessor Name>.Tag, such as tagSVO.Tag.

How to Do an Incremental Crawl

To do an incremental crawl:

  1. Update the Searchable View Object Search Plugin field (see Figure 15-43) to "oracle.apps.fnd.applcore.search.TagSearchPlugin", or as shown in Example 15-21, create a subclass so that you can incorporate your security rules.

    Ensure you add a parameter passing the service ID of the Search view object. This may be done by clicking the LOV symbol next to the Search Plugin field, shown in Figure 15-42. See Figure 15-44.

    Figure 15-44 Search Plugin

    Described in the surrounding text.

    Note that the Plugin Class Name is the same as that described in Adding Attachments Support to Global Search.

    There are two parameters, shown in Table 15-10, that may be passed to the extension.

    Table 15-10 Parameters that can be passed to the plug-in

    Parameter Required Description

    TAG_SERVICE_ID

    Yes

    Service ID of the Searchable view object. This value must match the value in the tag:taggingButton component and the service_id of the Searchable view object query.

    KEY_SPLITTER_CLASS

    No

    An optional class that extends oracle.apps.fnd.applcore.search.BaseKeySplitter.

    This is a strategy class for splitting the resourceId value into individual primary key attribute values. By default, oracle.apps.fnd.applcore.search.DefaultKeySplitter is used, which will split values based on a period separator (the applications standard). The separated PS attribute values are matched to the primary key columns in the order the primary key columns are defined in the view object flat table editor.

    For more flexible arrangements, teams can implement any scheme they want (such as name-value pairs) by creating their own key splitter class and setting this parameter.

  2. Start the SearchFeedServlet in the user interface project.

Example 15-19 Creating a Searchable View Object

SELECT LOOKUP_TYPE,
       VIEW_APPLICATION_ID,
       LANGUAGE,
       SOURCE_LANG,
       MEANING,
       DESCRIPTION,
       CREATED_BY,
       CREATION_DATE,
       LAST_UPDATED_BY,
       LAST_UPDATE_DATE,
       LAST_UPDATE_LOGIN,
       'oracle.apps.fnd.applcore.lookuptype' AS SERVICE_ID,
       lookup_type||'.'||to_char(view_application_id)||'.'||language||'.'||meaning as RESOURCE_ID
FROM FND_LOOKUP_TYPES_TL

Example 15-20 Setting up the Oracle WebCenter Portal Tag

<af:panelFormLayout id="pfl1">
   <tag:taggingButton serviceId="oracle.apps.fnd.applcore.lookuptype"
               resourceName="#{bindings.Description.inputValue}"
               resourceId="#{bindings.LookupType.inputValue}.#{bindings.ViewApplicationId.inputValue}.#{bindings.Language.inputValue}.#{bindings.Meaning.inputValue}"/>
   <af:region value="#{bindings.tagginglaunchdialog1.regionModel}"
                       id="r1"/>
   <af:panelLabelAndMessage label="#{bindings.LookupType.hints.label}"
                                   id="plam4">
     <af:outputText value="#{bindings.LookupType.inputValue}" id="ot9"/>
</af:panelLabelAndMessage>

Example 15-21 Creating a Subclass

package oracle.apps.fnd;
import oracle.apps.fnd.applcore.search.TagSearchPlugin;
public class WlsTestTagSearchPlugin
extends TagSearchPlugin
{
  // All implementation through super class, or override methods important to you.
  // Be careful if implementing
  //  public Iterator getChangeList(SearchContext ctx, String changeType)
  // to call super(ctx, changeType) to get the applcore functionality.
}

You now can crawl using the command-line script. A full crawl will be done first, then on subsequent crawls the incremental functionality will call the getChangeList() method.

15.6.6 How to Use the Actionable Results API with Oracle Fusion Applications Search

This section details how to set up ECSF searchable objects to use with Oracle Fusion Applications Search. For information about setting up your global search infrastructure, see Getting Started with Oracle Enterprise Crawl and Search Framework.

Figure 15-45 shows the result that will be produced (a single row in the search results table).

Figure 15-45 Search Results Example

Described in the surrounding text.

The terminology referred to in this result is:

  • Flat Table URL Action is the Action Link.

  • Title for Flat Table 1:Col1619:Col2619 is the Fixed Content.

  • Any other required information would be added later is the Variable Content.

  • Task Action 1 is Other Actions.

As shown in Figure 15-46, ECSF searchable objects support two distinct Action Types: URL and Task. See also Figure 15-47 and Figure 15-48.

Most Oracle Fusion Applications will use the Task type with specific named parameters to integrate with the UI Shell; however both types will work.

Figure 15-46 Search Properties Example

Described in the surrounding text.

Fixed Content

The Fixed Content is derived from the Search Properties Title field.

Variable Content

The Variable Content is derived from the Search Properties Body field.

15.6.6.1 Implementing the URL Action Type

Figure 15-47 shows a URL Action.

Figure 15-47 Search Result Actions - URL Action

Described in the surrounding text.

For URL Action Types, the Oracle Fusion Applications Search will open a new browser tab or window containing the URL. To configure this type, add a URL Search Result Action with these parameters.

  • A unique name

  • Action Type of URL

  • An Action Target to the required destination, including groovy substitution parameters

  • A Title

No Parameters are required; however a single iconURL parameter may be defined if an icon is required for the URL action. See Table 15-11.

Table 15-11 iconURL Parameter

Name Required Description

iconURL

No

URL of icon to show next to the Action. Can be a relative reference such as /media/search/mime_doc.gif or a full URL such as http://host:port/path/to/icon.gif.

The icon will be shown in the search results with the title given in the Title field. When clicked, a new browser tab or window will open with this URL.

15.6.6.2 Implementing the Task Action Type

Figure 15-48 shows a Task Action.

Figure 15-48 Search Result Actions - Task Action

Described in the surrounding text.

For Action Types of Task, the Oracle Fusion Applications Search will open a UI Shell tab in the current page, or a new page containing the task flow. To configure this type, add a Task Search Result Action with these parameters.

  • A unique name

  • Action Type of Task

  • A Title

Parameters are shown in Table 15-12. Note that, although this table resembles Table 15-13, it presents the use case that the majority of users will use. The information in Table 15-13 is for a very small use case.

The action will be shown in the search results with the title given in the Title field. When clicked, a new UI Shell tab window will open with this task flow. If the viewId parameter is for the current page, the UI Shell tab will be in the current page; otherwise the current page will be replaced with a new UI Shell page with the search result.

Note:

Do not enter double quotation marks around the groovy expressions; use single quotation marks instead.

Table 15-12 Task Action Type Parameters

Name Required Description

viewId

Y

Name of the page. This is shown in the browser URL bar. For example, in http://127.0.0.1:8989/context-root/faces/TestUIShellPage, it would be /TestUIShellPage (Note the leading slash).

pageParametersList

N

Parameters list for the page. This is a semicolon delimited String of name-value pairs. For example, "param1=value1;param2=value2"

taskFile

Y

Name of the task definition file. For example, /WEB-INF/task-flow-definition.xml. See Passing Parameters in Search.

taskName

Y

The task flow definition ID. Available from the task definition file <task-flow-definition> ID attribute. For example, <task-flow-definition id='task-flow-definition'> would be "task-flow-definition". See Passing Parameters in Search.

navTaskKeyList

N

Key list to pass into the task flow to open in the target workspace. This is a semicolon delimited string of keys or key-value pairs. For example "key1;key2=value2"

navTaskParametersList

N

Parameters list to pass in to the task flow to open in the target workspace. This is a semicolon delimited string of name-value pairs. For example "param1=value1;param2=value2"

iconURL

N

URL of an icon to show next to the Action. Can be a relative reference such as /media/search/mime_doc.gif or a full URL such as http://host:port/path/to/icon.gif.

toolTip

N

Tooltip of the action. This also is available for URL actions.

navTaskLabel

N

Label to show on the results tab. Set the tab title of the tab that is opened after clicking a search result action. If not set, it will use the Action Name (the value shown in the results).

webApp

Y

Attribute used to look up the host and port of the associated WorkArea or Dashboard from the Oracle Fusion Applications Functional Core (ASK) deployment tables. These tables are populated at deployment time through Oracle Fusion Functional Setup Manager tasks.

contextCode

N

Used for Recent Items striping of data by business line. This attribute will divide recent items into groups. Valid values are:

  • CommonDomain

  • BI

  • FIN

  • SCM

  • CRM

  • PRJ

  • HCM

  • PRC

  • IC

objectType

N

Used for Recent Items striping of data by business line. This is a further subdivision within the contextCode classification for the recent item object (such as opportunity, contact, lead, quote). If objectType is supplied, contextCode should be set.

objectName

N

User recognizable String for a given recent item instance. Used as string to match keystrokes entered in auto suggest field. This value will be in the language of the user at the point of navigation (that is, when the record is saved). It will not change if the user changes language. If objectName is supplied, contextCode and objectType also should be set.

FUSECardCode

N

Note: Required only if the user is to be directed to a a FUSE result page.

When navigating to FUSE, this is the globalItemNodeId of the global menu entry that describes the page to which to navigate.

If this parameter is specified, FUSEObjectCode and FUSETaskflowParamsList also should be specified; these three parameters are passed to the FndUIShellController.globalMenuNavigate() API:

public void globalMenuNavigate(String globalItemNodeId,
                               String taskItemNodeId,
                               String taskParametersList
                               FndMethodParameters methodParameters)

This can be added as a parameter-value pair using the Figure 15-48 dialog.

FUSEObjectCode

N

Note: Required only if the user is to be directed to a a FUSE result page.

When navigating to FUSE, this is the taskItemNodeId of the global menu entry that describes the page to which to navigate. If this parameter is specified, FUSECardCode and FUSETaskflowParamsList also should be specified; these three parameters are passed to the FndUIShellController.globalMenuNavigate():

public void globalMenuNavigate(String globalItemNodeId,
                               String taskItemNodeId,
                               String taskParametersList
                               FndMethodParameters methodParameters)

This can be added as a parameter-value pair using the Figure 15-48 dialog.

FUSETaskflowParamsList

N

Note: Required only if the user is to be directed to a a FUSE result page.

When navigating to FUSE, this is the FUSETaskflowParamsList of the global menu entry that describes the page to which to navigate. This parameter likely will contain a groovy expression that embeds the primary keys of the search result to allow navigation to the exact search result. If this parameter is specified, FUSECardCode and FUSEObjectCode also should be specified; these three parameters are passed to the FndUIShellController.globalMenuNavigate() API:

public void globalMenuNavigate(String globalItemNodeId,
                               String taskItemNodeId,
                               String taskParametersList
                               FndMethodParameters methodParameters)

This can be added as a parameter-value pair using the Figure 15-48 dialog.

FUSETaskFlowId

N (conditionally)

The taskFlowId of the required dynamic navigation results flow in the form:

/WEB-INF/oracle/apps/fnd/applcore/patterns/uishell/GlobalSearchDummyResultsTaskFlow.xml#GlobalSearchDummyResultsTaskFlow

FUSENavTaskKeyList

N

The task keylist used to identify dynamic tabs.

FUSENavTaskLabel

N

The label to show on dynamic tabs.

This information applies to the next three parameters: FUSETaskFlowId, FUSENavTaskKeyList and FUSENavTaskLabel.

Starting with Release 11, Applications Core ships a single FuseOverview page used by all Oracle Fusion applications. All content is supplied by the menus.

The pre-Release 11 navigation will continue to work, however there is a limitation on the taskflows that can be opened. That is addressed by these new parameters.

The navigation made by this call will only navigate to a defaultMain taskflow:

FndUIShellController.globalMenuNavigate(FUSECardCode, FUSEObjectCode, FUSETaskflowParamsList, null)

This is okay for some teams because they open the results in a popup using adfc navigation flow cases built into their flows.

For teams coming new to FUSE search in Release 11, the requirement is to open a dynamicMain taskflow, much the same as in classic UIShell.

With the addition of these parameters a user can be sent to a dynamicMain taskflow.

There are now two use-cases for configuring FUSE Task Action parameters:

  • The original defaultMain navigation:

    Enter FUSECardCode, FUSEObjectCode, FUSETaskflowParamsList parameters only.

  • New dynamicMain navigation:

    Enter FUSECardCode, FUSETaskflowParamsList, FUSETaskFlowId and, optionally, FUSENavTaskKeyList and FUSENavTaskLabel.

    Do not enter FUSEObjectCode.

The Classic UIShell and FUSE parameters can continue to co-exist in the same action.

Caution:

If you have a searchable view object with a task search action, the parameters passed to the task flow from FndUIShellController.navigate(...) will be Strings, not the native type of the view object attributes. You must ensure that these values are converted from their native type to a String (in the navTaskParametersList) and back correctly (in your task flow).

For Integer types, this is largely automatic (if you reference the parameter as a String in the task flow), but use caution for dates and decimals.

15.6.6.2.1 How to Determine the Navigation Target

Navigation depends on the defined parameters. You may have classic UIShell parameters, FUSE parameters (those starting with FUSE%) or both defined. The algorithm used to determine the navigation is:

TaskSearchAction tsa = ...get the clicked action ...
boolean isClassicNav = get value of profile FND_CLASSIC_INTERFACE. 
     if isClassicNav
       // try to navigate to UIShell.
       if tsa has UIShellParameters 
         UIShellNavigate(tsa);
       else if tsa hasFUSEParameters
         FUSEPlusNavigate(tsa);
       else
         // log error, no nav can be done.
        else
           // try to navigate to FUSE.
      if tsa has FUSEParameters
         FUSEPlusNavigate(tsa);
       else if tsa has UIShellParameters
         UIShellNavigate(tsa);
       else
         // log error, no nav can be done.
15.6.6.2.2 How to Implement Preferred Navigation

Global Search will read the standard profile option FND_CLASSIC_INTERFACE that is used to determine where the user wants to be, and use the appropriate parameters to navigate. If there no parameters for that style of page, then it tries to use the other style, as shown in this example:

TaskSearchAction tsa = ...get the clicked action ...
boolean isClassicNav = get value of profile FND_CLASSIC_INTERFACE. 
     if isClassicNav
       // try to navigate to UIShell.
       if tsa has UIShellParameters 
         UIShellNavigate(tsa);
       else if tsa hasFUSEParameters
         FUSEPlusNavigate(tsa);
       else
         // log error, no nav can be done.
        else
           // try to navigate to FUSE.
      if tsa has FUSEParameters
         FUSEPlusNavigate(tsa);
       else if tsa has UIShellParameters
         UIShellNavigate(tsa);
       else
         // log error, no nav can be done.

Global Search supports two parameters, applicationStripe and pageDefinitionName, in task search actions that, if they are present, change the definition of the other task action parameters used for navigation. These parameters all become "caret delimited." That is, instead of having one value per parameter, they have multiple values, and they are delimited by the caret "^" character. In this case, all parameters must have the same number of delimited parts.

This additional configuration allows you as the developer to set up different navigation targets for the same action. The actual target followed will be determined when the user clicks the result and will be based on a permissions check based on the applicationStripe and pageDefinitionName parameters. If these two parameters are not supplied, the other parameters will be used "as is," and navigation will be performed based on their values.

If the applicationStripe and pageDefinitionName parameter values are supplied, the algorithm used is the same as for tagging.

  • Divide all delimited parameters based at the caret, and produce an ordered list of targets that can be opened.

  • If there is only one target defined, use it with no permission check.

  • For each target, determine if the user can open the page and task flow.

  • If a target that can be opened is in the current view, use it.

  • Take the first target that can be opened.

Whatever the outcome of the permissions check, you must ensure that at least one target can be opened, otherwise users will be presented with a blank page when they click the search result.

The parameters and descriptions for Preferred Navigation are shown in Table 15-13. Note that, although this table resembles Table 15-12, it presents a more complicated use case in which developers want to do a security check and direct the user to the most secure end point (the first allowed one in the list). The meaning of these columns changes with the caret delimitation; that is, a caret-delimited list of the old values, as well as two new parameters. Most users need to use only the information in Table 15-12.

Table 15-13 Parameters for Preferred Navigation

Name Required Delimited by caret "^" Description

applicationStripe

N

Y

(This attribute is used for pages.) Check security of the page against the policies that are located in LDAP. The applicationStripe name must be the same as the stripe name of the LDAP policy store, which is the same as the web.xml application.name attribute. If this parameter is supplied, the pageDefinitionName parameter must be supplied also. Example: crm^hcm

pageDefinitionName

N

Y

A delimited string of page definition names. If this parameter is supplied, the applicationStripe parameter also must be supplied. Example: oracle.apps.view.pageDefs.Test1PageDef^oracle.apps.view.pageDefs.AnotherPageDef

viewId

Y

Y

Name of the page for the pillar. This is shown in the browser URL bar. For example, in http://127.0.0.1:8989/context-root/faces/TestUIShellPage, it would be "TestUIShellPagêAnotherUIShellPage".

webApp

Y

Y

Attribute used to look up the host and port of the associated Work Area or Dashboard from the ASK deployment tables. These tables are populated at deployment time through Functional Setup Manager tasks.

pageParametersList

N

Y

Parameters list for the page. This is a semicolon delimited string of name-value pairs. For example, "param1=value1;param2=value2^anotherParam1=value1;anotherParam2=value2"

taskFile

Y

Y

Name of the task definition file. For example, "/WEB-INF/task-flow-definition.xml^/WEB-INF/anothertask-flow-definition.xml". See Passing Parameters in Search.

taskName

Y

Y

The task flow definition ID that is available from the task definition file <task-flow-definition> id attribute. For example, <task-flow-definition id='task-flow-definition'> would be "task-flow-definition^another-task-flow-definition". See Passing Parameters in Search.

navTaskKeyList

N

Y

Key list to pass into the task flow to open in the target workspace. This is a semicolon delimited string of keys or key-value pairs. For example "key1;key2=value2^anotherKey1;anotherKey2=value2"

navTaskParametersList

N

Y

Parameters list to pass in to the task flow to open in the target workspace. This is a semicolon delimited string of name-value pairs. For example "param1=value1;param2=value2^anotherParam1=value1;anotherParam2=value2"

navTaskLabel

N

Y

The label to show on the results tab. (Set the tab title of the tab that is opened after clicking a search result action.) If not set, it will use the Action Name (the value shown in the results). For example: "Manage user^View User" (Note: This will be shown on the UI, so use resource bundles.)

iconURL

N

N

The URL of the icon to show next to the Action.It can be a relative reference, such as /media/search/mime_doc.gif or a full URL, such as http://host:port/path/to/icon.gif.

Tooltip

N

N

A tooltip of action. This also is available for URL actions.

15.6.6.3 Passing Parameters in Oracle Fusion Applications Search

Ordinarily, taskFlowID uses the format <path><name>.xml#<name>; for instance taskFlowID="/WEB-INF/CaseDetails.xml#CaseDetails". However, Oracle Fusion Applications Search has taskFile and taskName attributes as shown in Figure 15-48. The code will merge them, adding the "#," so they become <taskFile>#<taskName>.

Parameters always are passed as parameter name=value. Often, it is either a literal value or an expression to an attribute such as col1. Example 15-22 shows how to pass four parameters.

Example 15-22 Parameter Passing in Oracle Fusion Applications Search

<SearchResultActions>
  <Action
    Name="View Lookup Type"
    ActionType="Task"
    DefaultAction="true">
    <Title>
      <![CDATA["Lookup: " + Meaning]]>
    </Title>
    <ActionTarget>
      <![CDATA[null]]>
    </ActionTarget>
    <Parameters>
      <Parameter Name="navTaskParametersList">
        <Value>
          <![CDATA["lookupType=" + LookupType + ";viewApplicationId=" + ViewApplicationId + ";language=US;meaning=" + Meaning]]>
        </Value>
      </Parameter>
      <Parameter Name="webApp">
        <Value>
          <![CDATA['GlobalSearch']]>
        </Value>
      </Parameter>
      <Parameter Name="TaskFile">
        <Value>
          <![CDATA["/WEB-INF/LookupTypeSearchResultsTaskFlow.xml"]]>
        </Value>
      </Parameter>
      <Parameter Name="navTaskKeyList">
        <Value>
          <![CDATA["meaning=" + Meaning]]>
        </Value>
      </Parameter>
      <Parameter Name="TaskName">
        <Value>
          <![CDATA["LookupTypeSearchResultsTaskFlow"]]>
        </Value>
      </Parameter>
      <Parameter Name="viewId">
        <Value>
          <![CDATA["TestUIShellPage"]]>
        </Value>
      </Parameter>
    </Parameters>
  </Action>

15.6.6.4 Ordering the Other Actions

In the ECSF search UI in JDeveloper, it is possible to define no action, or a single default action.

If a default action is defined, it will be used. The other actions will be shown in sorted order based on task title. If no default action is defined, the first sorted action will be used as the default action.

This sorting mechanism is used because there is no way, using the current ECSF APIs, to provide a stable order of actions.

Due to this sorting mechanism, it is strongly recommended to have stable, sortable task titles (they may be groovy bound and therefore mutate based on an individual search result) to prevent confusing the end user.

15.6.6.5 Using Click Path and the Saved Search

When the user is using Oracle Fusion Applications Search before saving a search, he or she may perform several interactions with the UI including:

  • Expanding the attribute filters by selection (performs searches)

  • Narrowing the search terms

  • Opening unsearched groups (which performs searches in those groups)

  • Scrolling through results in a group

This is called the click path of the user.

When a search is saved, some of this information (the structural part at the tip of the click path) is saved, but prior actions and exact scroll positions are not. This means that when running a saved search, the following items are not restored to the user:

  • Exact expanded groups in the result at the time of save

  • Scroll positions within a group

  • Full LOV expansion state of attribute filters

When ECSF returns facet information, it returns facet entries only for the level below that which is selected. For example, if there are no filters, the facets will be shown correctly with one level of detail. If a first-level facet is selected, that selection will be shown, but not its siblings. If there are facets below that level, this next level will be shown as these are returned. As the user starts to refine or expand the attribute filters, the search filters will be filled in based on this new click path.

15.6.7 How to Integrate Non-Applications Data into Oracle Fusion Applications Search

Oracle Fusion Applications Search can also be used with non-standard ECSF searchable view objects.

15.6.7.1 Oracle Business Intelligence Integration

Oracle Business Intelligence results will be shown in a results area separate from Oracle Fusion Applications Search view object results. To implement this separation, Oracle Fusion Applications Search shows results in a multiple-tab format.

The tabs are named Applications, where all Search view objects and WebCenter Portal results will reside, as well as Business Intelligence. The split is performed at a category (or searchable groups) level, so you will see a consolidated list of categories in the multi-select category dropdown. These categories are split at search time.

The business rule that splits categories into the Oracle Business Intelligence table is a lowercase category_name, such as bi_%.

Although the formatting of Oracle Business Intelligence results will be slightly different, no developer action is required.

Oracle Secure Enterprise Search (SES) Setup

To set up SES, you need to set up these parameters:

  • Source

    See the Oracle Fusion Middleware Developer's Guide for Oracle Business Intelligence Enterprise Edition guide for how to create your Oracle Business Intelligence source. Define a source based on the Oracle EBusiness Suite R12, and give the following parameters:

    SES Source Configuration:
    Configuration URL: http://10.156.30.40:9704/bisearch/crawler/oracle.biee.search.BISearchableTreeObject/ConfigFeed?forceInitialCrawl=true
    User ID: Administrator
    Password: password
     
    Authorization Tab:
    HTTP endpoint for authorization: http://10.156.30.40:9704/bisearch/crawler/SecurityService
    User ID: Administrator
    Password: password
    Business Component: oracle.biee.search.BISearchableTreeObject
    Display URL Prefix: http://10.156.30.40:9704/bisearch/urlbuilder
     
    where the IP address is your Oracle Business Intelligence server installation, and the user name/password are for a sufficiently authorized Oracle Business Intelligence user.
     
    Leave all other values at default.
    
  • Source Group

    Create a source group (SES Searchtab then Source groups) and name it <some code name>.

    You may go into Global Settings and translate the group name so the users see a more recognizable name.

    Import the group as an external category into ECSF.

  • Searching

    When you click a link, you will be redirected to Oracle Business Intelligence. If you do not have a consolidated Oracle Internet Directory (OID) setup, you will be asked to log in again.

15.6.7.2 Ensuring Parity of Users

Users must be defined in multiple applications if you do not have a single authentication store.

Ensure that you have a user defined that is common across both Fusion Applications and any external applications such as Oracle WebCenter Portal or Oracle Business Intelligence.

For instance, you can create an fmwadmin user on the Oracle Fusion Applications side by adding to the jazn-data.xml file.

With Oracle Secure Enterprise Search (Oracle SES) Authentication pointing to the ECSF SearchFeedServlet, which is using the WebLogic Server container security, this user will be verified by the Oracle SES authentication callbacks.

In a true enterprise environment, both the Oracle Fusion Applications web container and WebCenter Portal would be set up with the same OID.

Using two different authentication stores will mean you get multiple logins when clicking results.

15.7 Implementing Auto-suggest

The fnd:searchSuggestBehavior tag implements suggestion functionality, often known as typeahead. It is added to an af:inputText field, and will display as a popup menu below the field as a user types into the field. Its primary use is in Global Search to show groupings of suggestions from various sources, and for CRM to show suggestions in its search fields. The search suggest configuration for Global Search is done using the setup task flows described in How to Control the Look and Feel of Search. The tag is available in JDeveloper under the Applications Components in the Component Palette, as shown in Figure 15-49.

Figure 15-49 Search Suggest in the Component Palette

Described in the surrounding text.

The suggestions in the group fall into two types of values:

  • Search suggestions. These are keywords on which the user wants to search, and when selected, these words will be placed in the search field.

    Note:

    Keywords can be deleted using the Oracle Enterprise Scheduler Service submission UI. No additional coding is required.

  • Shortcuts to other functionality. Selecting these entries will perform a specific function for that entry; the text will not be placed in the search field because it is not a keyword. For example, if the user types part of a Navigator entry, the Navigator link will be suggested to them; clicking it will navigate to that menu entry, just the same as if the user had selected the entry from the Navigator.

The fnd:SearchSuggestBehavior is developed as an Oracle ADF Behavior that closely follows the Oracle ADF version af:autoSuggestBehavior.

fnd:searchSuggestBehavior is similar in functionality to af:autoSuggestBehavior but adds:

  • the rendering of group headings and items

  • the expanded scrolling behavior to skip heading selection

  • selection into the field only of keyword entries (not menu items)

  • rendering of icons for each group and suggestion

It also adds to the communication to the server with new initialization, top suggestion and selection messages.

The user interacts with the suggestion list as follows:

  • Auto-suggest starts with the third typed character. This can be configured.

  • The user starts to type into the field against which the search suggest is defined.

  • If there are no matching suggestions in a section, the section is not shown.

  • As soon as the user selects the search field, a message is sent to the server to start to pre-load the lists of suggest items, so that by the time the user has typed the second character (expect a second or so), any delay on startup is done. As many groups as possible are loaded in a background thread to speed up the load of data.

  • A user can navigate in the popup by using arrow keys, Home and End to circle among entries. As they do this, the currently-selected entry will be highlighted, and if it is a keyword, it will be entered in the search field. If it is a Recent Item or a Favorite, it will not be placed in the search field because it is not a search term.

  • Pressing Enter will perform the search if the highlighted item is a search term. Otherwise, if it represents a navigation, the relevant task flow with parameters will be called. This can be configured.

  • The match done is a case insensitive instring. The matched substring is highlighted in bold.

  • A maximum of 25 items will be spread over the groups as evenly as possible. This can be configured.

  • Any item that is to open a URL in a new window will show as a URL in the popup. This is required to set target="_blank" so the URL opens in a new browser window or tab, and not by overwriting the current Fusion Applications window.

  • Groups are shown in the order defined in the JSPX tag.

Skinning keys are used for the items in the popup:

  • .FndSearchSuggestItem

  • .FndSearchSuggestItem:selected

  • .FndSearchSuggestGroup

  • .FndSearchSuggestGroup:first-child

  • .FndSearchSuggestItemNormal

  • .FndSearchSuggestItemCount

  • .FndSearchSuggestItemCount:rtl

  • .FndSearchSuggestItemHilight

The attributes of the fnd:searchSuggestBehavior tag are shown in Table 15-14.

Table 15-14 fnd:searchSuggestBehavior Attributes

Attribute Name EL capable? Required? Default Description

type

Yes

No

Popup

The type of search suggest behavior.

Valid values are inline and popup. Inline is where the suggestions will be placed in the content of an existing panel. This panel is identified by inlineSuggestionComponentId. The popup is the normal popup UI that will show below the search field. Popup is the default and the majority use case.

maxSuggestedItems

Yes, resolves to an integer

No

25

The maximum suggested items to be shown across all groups. Items will be evenly distributed across groups to achieve this.

minCharCount

Yes, resolves to an integer

No

3

The minimum number of characters that need to be typed before the suggestions are shown.

suggestGroup<x> where x is a number between 1 and 8

Note: suggetGroups is the preferred way to configure autosuggest.

Yes, resolves to a SearchSuggestGroup

Yes for suggestGroup1

No for suggestGroup2-8

N/A

A value that resolves to an oracle.apps.fnd.applcore.patterns.ui.behaviors.searchSuggest.SearchSuggestGroup, or a predefined group name.

A group corresponds to the subheadings and items shown in the suggestion list, and are in the order of <x>.

Predefined groups that can be referenced by code are:

searchterms, globalsavedsearches, globalrecentsearches, navigator, recentitems, favoriteitems, watchlist

These predefined groups are only available to use within UI Shell pages.

ContextCode<x> where x is a number between 1 and 8

Note: suggetGroups is the preferred way to configure autosuggest.

Yes

No

null

A context code for group x to provide meaning and subdivision of the group data. For Fusion Applications, these groups are expected. (For a custom group, this is group dependent.)

  • CommonDomain

  • BI

  • FIN

  • SCM

  • CRM

  • PRJ

  • HCM

  • PRC

  • IC

ObjectType<x> where x is a number between 1 and 8

Note: suggetGroups is the preferred way to configure autosuggest.

Yes

No

null

A further division under contextCode<x> to provide further meaning within group x. For Fusion Applications, a meaningful object is expected, such as Contracts.

suggestGroups

Yes. Resolves to public List<SearchSuggestGroupOption>

Yes

N/A

A list of Search Suggest Group Options. Each group option represents a group that will be shown in the autosuggest list, in the order created in the list.

typeDelay

Yes, resolves to an integer

No

500

The delay between keystrokes before the suggestions are fetched. The number is a time in milliseconds.

suggestionSourceId

Yes, resolves to a string

No

null

A string that is unique to this source of suggestions. If two or more fields are to use the same source of suggestions, they can use the same identifier. In this case, they must all have the same value for the suggestedGroups attribute.

suggestionSourceId will identify a session-level managed bean, so it must be unique in the range of session managed beans, and follow the rules of managed bean identifiers. If it is not set, it will use the clientId of the text component to which this behavior is added.

submitOnEnterComponentId

Yes, resolves to a string

No

null

A relative Component ID for an action Component that is to be submitted when the user presses Enter in this field. This provides the equivalent of pressing this button. If this is not set, pressing Enter will have no effect in the field, unless other listeners process this keypress.

inlineSuggestionComponentId

Yes, resolves to a string

No

Null

A relative Component ID for the suggestions to be placed instead of in a popup under the field. This attribute is required if type=inline. This component needs to have clientComponent=true set and should be visible=false.

This component needs to be a container component; panelGroupLayout is recommended.

inlineReplaceComponentId

Yes, resolves to a string

No

null

A relative Component ID representing a component to hide if type=inline. If set, this is a way to swap out the UI and replace it with that shown in inlineSuggestionComponentId.

This component needs to have clientComponent=true set.

topSuggestions

Yes, resolves to a string

No

On

Determines whether to show top suggestions when the field is first clicked. These suggestions are provided without filters and provide a starting point for the user. Once the user starts to type, regular filtered selections are shown. This is only processed if type=popup, as inline suggestions handle this using a dedicated panel identified by inlineSuggestionComponentId. Valid values are on and off. Off is the default.

postSelectionFocusComponentId

Yes, resolves to a string

No

null

A relative Component ID representing a component to give focus after a selection is made. This is primarily of use so that focus is no longer in the search field, and topSelections will be shown immediately when the user re-enters the field. By default, focus will remain in the search field.

adfPopupId

 

No

 

The relative component Id of an Oracle ADF popup in which inline suggestions are to be shown. This popup must have contentDelivery="immediate" and childCreation="immediate" so that it is immediately available on the client.

The Oracle ADF popup should have a panel with Id corresponding to inlineSuggestionComponentId and may have inlineReplaceComponentId and adfMessageComponentId components.

This attribute is only relevant when type="inline".

adfMessageComponentId

 

No

 

A relative Component Id of a component on which to set a "search for 'linda'" message. This attribute is only relevant when type="inline".

adfShowComponentIds

 

No

 

A space-separated list of relative Component Ids representing components to make visible once the loading suggestions message is finished. This attribute is only relevant when type="inline" and adfPopupId is set, and is a way of not allowing the user to select options on the inline popup that are not yet ready to be selected because their data is not initialized (for example clicking a button).

noMatchMessage

 

No

 

The message shown to the user when there are no matches. If this attribute is not set, no message will be shown, and any existing suggestions will be hidden.

groupIcons

 

No

 

Do you want to show the icons in group headings for all defined groups?

This is an override for the group-specific generation of icons in group headings. groupHeadings must be 'on' (the default) for this attribute to have any effect, because the icons are shown in the heading.

If a group does not have a heading icon, this setting will have no effect because there is nothing to show or hide. However if the group does have a heading icon, this setting will show or hide it. Valid values are 'on' and 'off'.

adfPopupId

 

No

 

This attribute is only relevant when type="inline".

The relative component Id of an ADF popup in which inline suggestions are to be shown. This popup must have contentDelivery="immediate" and childCreation="immediate" so that it is immediately available on the client.

The ADF popup should have a panel with Id corresponding to inlineSuggestionComponentId and may have inlineReplaceComponentId and adfMessageComponentId components.

adfMessageComponentId

 

No

 

This attribute is only relevant when type="inline".

A relative component Id of a component on which to set a "search for search_term" message.

adfShowComponentIds

 

No

 

A space-separated list of relative component Ids representing components to make visible once the loading suggestions message is finished. This attribute is only relevant when type="inline" and adfPopupId is set, and is a way of not allowing the user to select options on the inline popup that are not yet ready to be selected because their data is not initialized (for example clicking a button).

noMatchMessage

 

No

 

The message shown to the user when there are no matches. If it is not set, no message will be shown and any existing suggestions will be hidden.

These will be used by Global Search in the 'Global Search Field with Results Popup' taskflow to show suggestions and to provide personalization UI (by clicking the gear icon).

Additional behaviors are:

  • Top Suggestions Loading. If topSuggestions='on' and this is the first time the search field has been clicked, then static loading UI will be shown.

  • Show More. If there is more than one group of suggestions and if any of those groups have been truncated to fit within the maximum number of suggestions, a Show More link will be shown on each group. Once clicked, only this group's selections will be shown with a Show Less link.

  • If no suggestions are found, the noMatchMessage is shown. If this is empty or null, the suggest popup will not be shown. If it is already open, it will be closed. If type='inline', the suggest content will be hidden; if inlineReplaceComponentId is specified, this content will be re-shown.

  • If adfMessageComponentId is specified, a message "Search for '<contents of search field>'" will be set as the text of that component (on the client).

  • If adfShowComponentIds are specified, these will be made visible after the top suggestions loading banner has been shown.

  • If in screen reader mode, fnd:searchSuggestBehavior will no longer be disabled if type='inline' (it is still disabled if type = 'popup').

  • Top suggestions will be shown all the time using the defined suggest groups. This means the suggest content is completely consistent and symmetrical for top and autosuggest content in both types of popup and inline.

15.7.1 How to Implement fnd:searchSuggestBehavior

To implement fnd:searchSuggestBehavior, the basic developer steps are:

  • Drag-and-drop the component onto an af:inputText field.

  • Set the appropriate attributes. suggestGroups is required.

  • If creating a custom group, extend AbstractSearchSuggestGroup, and reference it using Expression Language.

These examples show how you can implement fnd:searchSuggestBehavior.

Using suggestGroups containing a predefined and custom group

<af:inputText id="SearchField"
value="#{pageFlowScope.searchString}"
maximumLength="2048"
required="false" simple="true" autoComplete="off"
clientComponent="true" usage="search">
<fnd:searchSuggestBehavior suggestGroups="#{SomeBean.searchSuggestBehaviorGroups}"/>
</af:inputText>
 
/**
* Get the Search Behavior groups.
* This is based on the GlobalSearchOptions set for the current page. If no
* options are set it defaults based on the method
* GlobalSearchOptions.getDefaultGlobalSearchOptions().
* @return List of behavior groups.
*/
public List<SearchSuggestGroupOption> getSearchSuggestBehaviorGroups()
{
List<SearchSuggestGroupOption> ret = new ArrayList<SearchSuggestGroupOption>();
ret.add(new SearchSuggestGroupOption(KeywordSearchSuggestGroup.NAME));
ret.add(new SearchSuggestGroupOption("#{SomeBean.customGroup}",
GroupHeading.ON,
"/path/to/icon.png",
"Group Heading",
null,
null));
 
return ret;
}
 
public AbstractSearchSuggestGroup getCustomGroup()
{
// This group will have any heading name, icon,
// context code and object code set on it by the framework
// to override code (developer set) values.
return new MyCustomSearchSuggestGroup();
}

Simple popup with all predefined groups

<af:inputText label="Simple Default: " id="simple">
   <fnd:searchSuggestBehavior suggestGroup1="searchterms"
                     suggestGroup2="globalsavedsearches"
                     suggestGroup3="globalrecentsearches"
                     suggestGroup4="navigator"
                     suggestGroup5="recentitems"
                     suggestGroup6="favoriteitems"
                     suggestGroup7="watchlist"/>
</af:inputText>

Popup with 3-second delay

<af:inputText label="3 second delay :" id="threesec">
   <fnd:searchSuggestBehavior typeDelay="3000"
                suggestGroup1="searchterms"
                suggestGroup2="globalsavedsearches"
                suggestGroup3="globalrecentsearches"
                suggestGroup4="navigator"
                suggestGroup5="recentitems"
                suggestGroup6="favoriteitems"
                suggestGroup7="watchlist"/>
 </af:inputText>

Popup with maximum of 10 suggestions

<af:inputText label="10 maximum items: " id="tenitems">
    <fnd:searchSuggestBehavior maxSuggestedItems="10"
               suggestGroup1="searchterms"
               suggestGroup2="#{SuggestGroupFactory.bookSearchSuggestGroup}"/>
</af:inputText>

Popup with submit on Enter

<af:inputText label="Search on Enter:" id="searchonenter">
     <fnd:searchSuggestBehavior submitOnEnterComponentId="cb1"
                  suggestGroup1="searchterms"
                  suggestGroup2="globalsavedsearches"
                  suggestGroup3="globalrecentsearches"
                  suggestGroup4="navigator"
                  suggestGroup5="recentitems"
                  suggestGroup6="favoriteitems"
                  suggestGroup7="watchlist"/>
   </af:inputText>
   <af:popup id="p1">
     <af:outputText value="Button Clicked!" id="ot1"/>
   </af:popup>
   <af:commandImageLink id="cb1" partialSubmit="true" immediate="true"
          shortDesc="Search"
          icon="#{fnd:bidiImage('/images/applcore/uishell/globalSearch/go_ena.png')}"
          hoverIcon="#{fnd:bidiImage('/images/applcore/uishell/globalSearch/go_ovr.png')}"
          depressedIcon="#{fnd:bidiImage('/images/applcore/uishell/globalSearch/go_dwn.png')}">
     <af:showPopupBehavior popupId="p1" align="afterStart"
                           alignId="cb1"/>
   </af:commandImageLink>

Common data for two suggestion popups

<af:inputText id="it6" label="common behavior 1">
   <fnd:searchSuggestBehavior suggestionSourceId="common1"
                suggestGroup1="searchterms"
                suggestGroup2="navigator"/>
 </af:inputText>
 <af:inputText id="it7" label="common behavior 2">
   <fnd:searchSuggestBehavior suggestionSourceId="common1"
                suggestGroup1="searchterms"
                suggestGroup2="navigator"/>
 </af:inputText>

Custom group with topSuggestions

<af:inputText id="topSuggestions" label="Top Suggestions: " value="seahawk">
   <fnd:searchSuggestBehavior suggestGroup1="#{SuggestGroupFactory.bookSearchSuggestGroup}"
                     topSuggestions="on"/>
 </af:inputText>
Where SuggestGroupFactory is some Expression Language-reachable managed bean:
public class SuggestGroupFactory
implements Serializable
{
  public SearchSuggestGroup getBookSearchSuggestGroup()
  {
    return new BookSearchSuggestGroup();
  }

Group that uses object striping

<af:inputText id="crmRIMenu1" label="CRM RI Menu" value="Lin">
    <fnd:searchSuggestBehavior suggestGroup1="recentitems"
                contextCode1="CRM"
                objectType1="Search"/>
 </af:inputText>

Group that hides and shows content in the base page

The following suggestion will be shown in the "suggestions" panelgrouplayout. The content in "swapout" will be hidden when suggestions are shown.

<af:inputText id="suggestinline" label="Suggest inline:">
   <fnd:searchSuggestBehavior inlineSuggestionComponentId="suggestions"
      type="inline"
      inlineReplaceComponentId="swapout"
      suggestGroup1="#{SuggestGroupFactory.bookSearchSuggestGroup}"
      contextCode1="Book"
      objectType1="theseahawk.txt"
      noMatchMessage="No suggestions found."
      minCharCount="3"/>
   </af:inputText>
<af:panelGroupLayout layout="vertical" id="suggestions" rendered="true"
   styleClass="fndGlobalSearchSuggestPanel"
   clientComponent="true" visible="false">
   <!-- Autosuggest content will be added here. -->
</af:panelGroupLayout>
<af:panelGroupLayout layout="vertical" id="swapout" rendered="true"
   clientComponent="true"
>
<af:outputText value="Fe fi fo fum, here is some text that will soon be gone."
   id="ot2"/>
</af:panelGroupLayout>

15.7.2 How to Implement a Custom Suggestion Group

If you wish to create your own list of suggestions to show the user, you need to create a SearchSuggestGroup and register it with the af:searchSuggestBehavior tag for the af:inputText field that will show the suggestions.

Before you begin, you must implement the oracle.apps.fnd.applcore.patterns.ui.behaviors.searchSuggest.SearchSuggestGroup interface by extending the AbstractSearchSuggestGroup. Do not extend the interface directly; this is not supported.

This interface is available in the UIComponents-View.jar, which is available by including the Applications Core (ViewController) library in your project. This dependency should be met already.

The developed class, and all resources it needs, should be available to all applications that consume the functionality. For example, for CRM implementation, it should be available in common CRM libraries. URL references used for icons in the group headings, or for suggestion items, should not be local to the web application unless the icon comes from a common icon repository that is available across web applications.

BookSearchSuggestGroup

This is an example implementation that loads a book from a text file, word counts it and provides the words as suggestions.

 public class BookSearchSuggestGroup
extends AbstractSearchSuggestGroup
{
  /**
   * Unique name of this group. Must be unique across all groups.
   */
  public static final String NAME = "booksearchterms";
  private List<SearchSuggestItem> _keywords = new ArrayList<SearchSuggestItem>();
 
  public BookSearchSuggestGroup()
  {
    super();
  }
 
  @Override
  public String getDisplayName()
  {
    return "My Book Terms";
  }
 
  @Override
  public String getIconURL()
  {
    return "images/applcore/uishell/globalSearch/search_ena.png";
  }
 
  @Override
  public List<SearchSuggestItem> getItems(String searchText,
                                          int maxSuggestedItems)
  {
    long now = System.currentTimeMillis();
    List<SearchSuggestItem> ret = new ArrayList<SearchSuggestItem>();
    
    String upperFilter = searchText.toUpperCase();
    int size = _keywords == null ? 0 : _keywords.size();
    for (int i = 0; i < size; i++)
    {
      SearchSuggestItem kw = _keywords.get(i);
      String keyword = kw.getText();
      String upperKw = keyword.toUpperCase();
      int index = upperKw.indexOf(upperFilter);
      if (index >= 0)
      {
        ret.add(new SearchSuggestItem(keyword, kw.getCount()));
        if (ret.size() >= maxSuggestedItems)
        {
          break;
        }
      }
    }
    
    if (AppsLogger.isEnabled(AppsLogger.FINEST))
    {
      long later = System.currentTimeMillis();
      long duration = later - now;
      AppsLogger.write(this,
                       "Global Search: BookSearchSuggestGroup, getItems: Found: " + ret.size() + " items for: " + searchText + " in " + duration +
                       " mSecs.",
                       AppsLogger.FINEST);
    }
    return ret;
  }
 
  @Override
  public List<SearchSuggestItem> getTopSuggestions(String searchText,
                                                   int maxSuggestedItems)
  {
    long now = System.currentTimeMillis();
    List<SearchSuggestItem> ret = new ArrayList<SearchSuggestItem>();
    
    ret.add(new SearchSuggestItem("sea"));
    ret.add(new SearchSuggestItem("seahawk"));
    ret.add(new SearchSuggestItem("Rafael"));
    ret.add(new SearchSuggestItem("Sabatini"));
    ret.add(new SearchSuggestItem("pirate"));
    ret.add(new SearchSuggestItem("English"));
    ret.add(new SearchSuggestItem("Spanish"));
    ret.add(new SearchSuggestItem("privateer"));
    ret.add(new SearchSuggestItem("Armada"));
    
    Collections.sort(ret);
        
    if (AppsLogger.isEnabled(AppsLogger.FINEST))
    {
      long later = System.currentTimeMillis();
      long duration = later - now;
      AppsLogger.write(this,
                       "Global Search: BookSearchSuggestGroup, getTopSuggestions: Found: " + ret.size() + " items in " + duration +
                       " mSecs.",
                       AppsLogger.FINEST);
    }
    return ret;
  }
 
  @Override
  public String getName()
  {
    return NAME;
  }
   
  @Override
  public void init(Map<String, Object> attributes)
  {
    long now = System.currentTimeMillis();
 
    Map<String, WordData> words = loadResource("/oracle/apps/view/backing/theseahawk.txt");
    List<SearchSuggestItem> keywords = new ArrayList<SearchSuggestItem>(words.size());
    Iterator<WordData> wordsIter = words.values().iterator();
    while (wordsIter.hasNext())
    {
      WordData word = wordsIter.next();
      keywords.add(new SearchSuggestItem(word.word, word.count));
    }
    
    Collections.sort(keywords);
    _keywords = keywords;
    if (AppsLogger.isEnabled(AppsLogger.INFO))
    {
      long later = System.currentTimeMillis();
      long duration = later - now;
      System.out.println("Global Search: BookSearchSuggestGroup, init: loaded " + _keywords.size() + " keywords in " + duration +
                       " mSecs.");
      AppsLogger.write(this,
                       "Global Search: BookSearchSuggestGroup, init: loaded " + _keywords.size() + " keywords in " + duration +
                       " mSecs.", AppsLogger.INFO);
    }
  }

15.7.3 How to Implement fnd:searchSuggestBehavior in Global Search

The Search Suggest functionality has been implemented in Applications Core Global Search; there is nothing the developer needs to do to get this feature.

By default, the user will see predefined sources of suggestions as described in each suggestion group described in Table 15-15.

Table 15-15 Search Suggestions Sections

Section Live data Supports Striping using contextCode, objectType

Search Terms

Essentially Yes. Data is loaded at start but updated in memory.

Data is saved to table on each Search.

No

Recent Items

Yes. Data is the same as that in the Recent Items list.

Data is updated live in the list and saved on logout.

Yes

Favorite Items

No, loaded up front.

No

Navigator

No, loaded up front.

Yes

Watchlist

No, loaded up front.

No

Saved Global Searches

Yes

No

Recent Global Searches

Yes

No

My Search Terms

Alternate search words that will be placed in the search field if the user clicks them.

The user then can perform a search by pressing Enter or clicking the Search icon.

The keywords are those from previous unfiltered searches by the user, using the result count of that search. These values are stores in a new table:

CREATE TABLE FND_FUSION_SEARCH_KEYWORDS
(
    FUSION_SEARCH_KEYWORDS_ID NUMBER(18,0) NOT NULL,
    USER_ID VARCHAR2(64 CHAR) NOT NULL,
    KEYWORDS VARCHAR2(260 CHAR) NOT NULL,
    SEARCH_COUNT NUMBER(18,0) NOT NULL,
    RESULT_COUNT NUMBER(18,0),
    ENTERPRISE_ID NUMBER(18,0) DEFAULT NVL(SYS_CONTEXT('FND_VPD_CTX', 'ENTERPRISE_ID'), 1) NOT NULL,
    CREATION_DATE TIMESTAMP (6) NOT NULL,
    CREATED_BY VARCHAR2(64 CHAR) NOT NULL,
    LAST_UPDATE_DATE TIMESTAMP (6) NOT NULL,
    LAST_UPDATED_BY VARCHAR2(64 CHAR) NOT NULL,
    LAST_UPDATE_LOGIN VARCHAR2(32 CHAR),
    CONSTRAINT FND_FUSION_SEARCH_KEYWORDS_PK PRIMARY KEY (FUSION_SEARCH_KEYWORDS_ID, ENTERPRISE_ID) DISABLE
);

My Recent Items

Recent Items from the recent items menu that match the search String.

When the user clicks a recent item, the task flow opens up directly.

The value shown to the user is the objectName, if present, otherwise it is the taskflow label (what is shown in the Recent Items UI). Striping is possible using contextCode and objectType if present. Note that Global Search does not use the striping values.

My Favorite Items

This is similar to recent items, however the favorite items need to be loaded from tables when first used and are thereafter cached in memory.

My Navigator

The Navigator items are loaded by using the same APIs the navigator menu uses, and will reuse any loaded or cached menu structure.

The value shown is the itemNode label. Striping is possible using contextCode and objectType if present. Note that Global Search does not use the striping values.

Support entries that are navigation targets (the FunUIShellController.navigate(...)), as well as pure URLs (<a href = ...) that open in a new frame. URLs will be shown in the popup as URLs; that is, with the underline.

My Watchlist

Watchlist items are loaded when first referenced.

Clicking an item expands into the watchlist item.

Saved Searches

Saved searches are referenced directly from Global Search.

Clicking an item runs the search.

Recent Searches

Recent searches are referenced directly from Global Search.

Clicking an item runs the search.

15.7.4 How to Implement Dynamic UI Support for searchSuggestBehavior

Dynamic UI support for searchSuggestBehavior satisfies these three requirements:

  1. Dynamic configuration adds Java level configuration for fnd:searchSuggestBehavior. This allows developers to add input text fields dynamically through code using metadata available in MDS.

  2. Enables table support for fnd:searchSuggestBehavior so, for example, developers can add flexfields to tables. fnd:searchSuggestBehavior works in repeating and stamped structures, and with various combinations of rowSelection.

  3. Can show one set of suggestions but return a different value to the field. To provide alias lookup support, suggestions will be shown only when a prefix character ('#' by default) is typed. The suggestions shown are aliases and may show different values; they will not show the prefix character. When a suggestion is picked, the prefixed value is returned to the field.

User Interaction

User interaction for requirements 1 and 2 are the same as the existing fnd:searchSuggestBehavior solution. That is, the user clicks in an af:inputText field, the popup shows, and the user selects from it.

For requirement 3, the interaction is:

  • User selects the field and types the prefix character ('#', though it could be anything). If the user does not type the prefix character, no suggestions are shown.

  • User types another character and autosuggest shows suggestions based on the second character. The '#' is not part of the suggestions, which may not even contain the characters typed.

  • As the user continues to type, the suggestions narrow.

  • User selects a value and an alias value for the selection is returned to the field. This value will contain the prefix character and may not look like the suggestion selected.

Requirement 1 - Dynamic Configuration Design

Provides a factory and a bean for setting all suggest attributes, and the ability to apply to an af:inputText field.

class: SearchSuggestBehaviorFactory

method: createSearchSuggestBehaviorBean()

Class SearchSuggestBehaviorBean is obtained from SearchSuggestBehaviorFactory.

methods:

  • applyTo(RichInputText it)

  • setXXXX(ValueExpression ve) (1 method per attribute you can see in the property inspector for fnd:searchSuggestBehavior).

Use as shown in this example.

RichInputText it = .. somehow get an input text field.
SearchSuggestBehaviorBean ssb = SearchSuggestBehaviorFactory.createSearchSuggestBehaviorBean();
ssb.setSuggestGroup1(ssb.createValueExpression("oracle.apps.fnd.searchSuggest.ReflectiveSearchSuggestGroup"));
ssb.setContextCode1(ssb.createValueExpression("ContextCode" + numChildren));
ssb.setObjectType1(ssb.createValueExpression("ObjectCode" + numChildren));
ssb.setTopSuggestions(ssb.createValueExpression("on"));
... set other attributes as appropriate ...
ssb.applyTo(it);

Requirement 2 - Enable Table Support

The developer can get the clientId of the input text field on which the search suggest field is configured.

For tables and other repeating structures, the af:inputText field and fnd:searchSuggestBehavior are defined once in a column (for an af:table) and only exist once in the server-side JSF component tree. When the markup is rendered, it is stamped out repeatedly for the number of rows in question. Numeric Ids are added into the client ID of the component (:0 for row 0, :1 for row 1, and so on).

The following APIs are added to AbstractSearchSuggestGroup (the class all suggestion group writers must extend):

/**
 * Set the suggestFieldClient Id. This is called by the search suggest framework
 * when suggestions are requested for a field, and before the groups are polled for
 * those suggestions. This value can be used to distinguish between multiple fields
 * sharing the same suggestion data, and perhaps different columns in a table.
 * @param suggestFieldClientId client id, for example pt1:USma:0:MAt2:1:pt1:t1:1:it1
 */
 
public void setSuggestFieldClientId(String suggestFieldClientId)
{
  _suggestFieldClientId = suggestFieldClientId;
}
 
/**
 * Get the suggestFieldClient Id. This will be set by the default init() method,
 * and updated on every click in the suggest field.
 * @return suggest field client id of the form a:b:c.
 */
public String getSuggestFieldClientId()
{
  return _suggestFieldClientId;
}

The suggestion group developer then can use logic similar to that below to determine the row. If the developer is using a table with rowSelection="single", then common rowSelectionAPIs can be used (id1 below). However if any other rowSelection method is used, this will be unreliable. A variant of Method 2, below, can be used.

/**
 * Use the clientId to find the InputText component, the enclosing table and
 * thereby the selected row.
 * @return selected row, -1 if cannot find for any reason.
 */
private int getSelectedRow()
{
  // There are 2 ways to find the cell: one is to know which column we are
  // in by being told (I use contextCode here) and determining the row using
  // the table's selected row. However this requires the table to have
  // rowSelection="single" which is likely but not always the case.
  // The second is to use the clientId of the component and a bit of internal
  // knowledge of the encoding of the ids.
   
  int id1 = getSelectedRowUsingTable();
  int id2 = getSelectedRowUsingClientId();
  System.out.println("TableCellSuggestGroup: getSelectedRow: found row using table selected row: " + id1 +
                       ", by clientId: " + id2);
  if (id1 != id2)
  {
    if (AppsLogger.isEnabled(AppsLogger.WARNING))
    {
      AppsLogger.write(this,
                       "TableCellSuggestGroup: getSelectedRow: found differing rows, row using table selected row: " + id1 +
                       ", by clientId: " + id2,
                       AppsLogger.FINEST);
    }
  }
  // client id is more accurate across different rowSelection attribute settings.
  return id2;
}
 
private int getSelectedRowUsingTable()
{
  // Method 1:
  UIComponent field = SearchUtils.findComponentByClientId(_searchFieldClientId);
 
  if (field != null)
  {
    RichTable table =
      (RichTable)PatternsUtil.getParentByType(field, RichTable.class);
 
    if (table != null)
    {
      // find the selected row.
      RowKeySet selectedRowKeys = table.getSelectedRowKeys();
      if (selectedRowKeys != null && selectedRowKeys.size() > 0)
      {
        // This works for Java ArrayLists, but will not work for BC collections.
        Integer row = (Integer)selectedRowKeys.iterator().next();
        return row;
      }
    }
  }
  return -1;
}
 
private int getSelectedRowUsingClientId()
{
  // Method 2:
  String clientId = getSuggestFieldClientId();
  String[] clientIds = clientId.split(":");
  if (clientIds.length > 2)
  {
    return Integer.valueOf(clientIds[clientIds.length - 2]);
  }
   
  return -1;
}

Requirement 3 - Alias Lookup Design

This requirement is met by the suggestion group developer, with one enhancement to allow the transposition of the display and Id values.

fnd:searchSuggestBehavior supports the ability to have a displayed value with a different id behind the scenes. It is achieved by the suggestion group developer adding suggestions using one of the more detailed constructors of SearchSuggestItem, using a different Id and text:

/**
 * Create an Item.
 * @param text translatable text shown to user.
 * @param id unique identifier.
 * @param count Optional count.
 * @param url fully qualified url the user can click. Must be of the form http://
 * it will result in a html href.
 */ 

public SearchSuggestItem(String text, String id, Number count, String url)

Until this point, the Id could be used only when the AbstractSuggestGroup suggestions were submitted to the server as a shortcut, such as selecting a navigator menu item.

In this case the selected suggestion was not placed in the search field (this is achieved by over-riding requiresSubmit() and returning true).

If different Ids and text were returned as suggestions and requiresSubmit() returned false, the text would be shown and placed into the search field when selected. This is the typical search terms use case commonly seen with auto suggest.

A new method added to the AbstractSuggestGroup indicates that the value to be returned to the field is the Id and not the text. This can be used only when requiresSubmit() is false:

/**
 * Given placeSelectedItemInField() returns true, do we place the SearchSuggestItem
 * Id in the search field, or in the display value.
 * 
 * It is almost always the case that the display value will be used. However
 * it is possible to perform a transposition where the user sees one thing
 * in the list of the suggestions, but another is returned to the field.
 * An example of this is for typing aliases. The user may type a prefix
 * character into the field to trigger suggest, such as '#'. The suggest group
 * responds only when # is entered and it will then suggest a list of descriptive
 * values without the '#' (they may be completely different to what is typed). 
 * When the value is selected by the user, the #value is placed in the field,
 * not the value seen in the suggest popup.
 * If this is set to true and no Id is set, then the display value is used as normal.
 * This does not apply if requiresSubmit() is true. In this case, if it is available,
 * the Id is always sent.
 * @return true if to place the id and not the display value in the field when
 * selected.
 * @see #placeSelectedItemInField
 * @see #requiresSubmit
 * @see SearchSuggestItem
 */
public boolean placeSelectedItemIdInField()
{
  return false;
}

The suggestion group developer has complete control of the rest of the requirements for this use case:

  • In the getItems(String searchText, int maxSuggestedItems) method, only return suggestions when the searchText starts with the prefix character.

  • Configure the fnd:SearchSuggestBehavior to start the suggestion at character 2 (the minCharCount attribute).

  • Return SearchSuggestItems with a different Id and text. The text will be shown in the suggestion list.

  • Override placeSelectedItemIdInField and return true to put the Id, not the text, into the field when selected.

Example Dynamic Suggestion Group in a Form Field

public void addTextField(ActionEvent actionEvent)
{
  FacesContext context = FacesContext.getCurrentInstance();
  Application application = context.getApplication();
 
  UIComponent btn = actionEvent.getComponent();
  UIComponent pfl = PatternsUtil.getParentByTypeAndId(btn, RichPanelFormLayout.class, "suggestPanel");
 
  int numChildren = pfl.getChildren().size();
  RichInputText it = (RichInputText) application.createComponent(RichInputText.COMPONENT_TYPE);
  it.setId("it" + numChildren);
  ValueExpression value = createValueExpression("#{pageFlowScope.DynamicSuggestInFormBean.it" + numChildren + "}");
  it.setValueExpression("value", value);
  it.setLabel("Input Text " + numChildren + ":");
  it.setImmediate(true);
  MethodBinding vcl = createMethodBinding("#{pageFlowScope.DynamicSuggestInFormBean.valueChanged}");
  it.setValueChangeListener(vcl);
 
  SearchSuggestBehaviorBean ssb = SearchSuggestBehaviorFactory.createSearchSuggestBehaviorBean();
  ssb.setSuggestGroup1(ssb.createValueExpression("oracle.apps.fnd.searchSuggest.PrefixedSearchSuggestGroup"));
  ssb.setContextCode1(ssb.createValueExpression("ContextCode" + numChildren));
  ssb.setObjectType1(ssb.createValueExpression("ObjectType" + numChildren));
  ssb.setTopSuggestions(ssb.createValueExpression("on"));
  ssb.setMinCharCount(new ConstantExpression(2));
  ssb.applyTo(it);
   
  pfl.getChildren().add(it);
}

Example Dynamic Suggestion Group in a Table Field

public void generateContent(ActionEvent e)
{
  _contentGenerated = true;
  for (int i = 0; i < _numTextFields; i++)
  {
    FacesContext context = FacesContext.getCurrentInstance();
    Application application = context.getApplication();
 
    UIViewRoot uiRoot = context.getViewRoot();
 
    RichColumn col = (RichColumn)PatternsUtil.findComponentByTypeAndId(uiRoot, RichColumn.class, "c" + i);
    RichInputText it = (RichInputText) application.createComponent(RichInputText.COMPONENT_TYPE);
    it.setId("it" + i);
    ValueExpression value = createValueExpression("#{row.it" + i + "}");
    it.setValueExpression("value", value);
     
    SearchSuggestBehaviorBean ssb = SearchSuggestBehaviorFactory.createSearchSuggestBehaviorBean();
    ssb.setSuggestGroup1(ssb.createValueExpression("oracle.apps.fnd.searchSuggest.TableCellSuggestGroup"));
    ssb.setContextCode1(ssb.createValueExpression("" + i));
    ssb.setObjectType1(ssb.createValueExpression("ObjectCode" + i));
    ssb.setTopSuggestions(ssb.createValueExpression("on"));
    ssb.applyTo(it);
 
    col.getChildren().add(it);
  }
}

Example of a Table Cell Search Suggestion Group

package oracle.apps.fnd.searchSuggest;
  
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.faces.component.UIComponent;
import oracle.adf.view.rich.component.rich.data.RichTable;
  
import oracle.apps.fnd.applcore.globalSearch.ui.SearchUtils;
import oracle.apps.fnd.applcore.log.AppsLogger;
import oracle.apps.fnd.applcore.patterns.ui.behaviors.searchSuggest.AbstractSearchSuggestGroup;
import oracle.apps.fnd.applcore.patterns.ui.behaviors.searchSuggest.SearchSuggestItem;
import oracle.apps.fnd.applcore.patterns.ui.util.PatternsUtil;
  
import org.apache.myfaces.trinidad.model.RowKeySet;
  
/**
 * A suggest group for placing in a table cell.
 * It uses the contextCode as the column number, and will determne the row from
 * the selected row of the current table.
 */
 
public class TableCellSuggestGroup
extends AbstractSearchSuggestGroup
{
  /**
   * Unique name of this group. Must be unique across all groups.
   */
  public static final String NAME = "TableCellSuggestGroup";
  private static final String _NUMBERS = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
  private String _searchFieldClientId = null;
  
  public TableCellSuggestGroup()
  {
    super();
  }
  
  /**
   * {@inheritDoc}
   */
  @Override
  public String getDisplayName()
  {
    String overRiddenDisplayName = super.getDisplayName();
    if (overRiddenDisplayName != null)
    {
      return overRiddenDisplayName;
    }
    return "Table Cell Suggestions";
  }
     
  /**
   * {@inheritDoc}
   */
  @Override
  public String getName()
  {
    String overRiddenName = super.getName();
    if (overRiddenName != null)
    {
      return overRiddenName;
    }
  
    return NAME;
  }

  /**
   * Initialize the group. We take a copy of the client Id of the input text field
   * that was clicked (could be on any row). The behavior is registered on the inputText
   * field, of which there is only one, so even though the clientId will be different
   * based on row stamping, it can be used to obtain the parent table later on.
   * @param attributes All attributes from the behavior, as well as the client Id
   * of the text field that initiated the initialization of the behavior server logic.
   */
  @Override
  public void init(Map<String, Object> attributes)
  {
    super.init(attributes);
    // Grab the client id, used for determining the row.
    _searchFieldClientId = (String)attributes.get("clientId");
  }
  
  /**
   * Get suggestions from user typing. In this case, it will return the row and column
   * of the cell clicked, then the search text itself, and then a number
   * of suggestions based on the searchText appended with numbers.
   * @param searchText user typed text.
   * @param maxSuggestedItems maximum suggestions to bother returning, any more will be ignored.
   * @return Suggestions to show the user.
   */
  public List<SearchSuggestItem> getItems(String searchText,
                                          int maxSuggestedItems)
  {
    List<SearchSuggestItem> ret = new ArrayList<SearchSuggestItem>();
    ret.add(new SearchSuggestItem(getCell()));
    ret.add(new SearchSuggestItem(searchText));
     
    for (int i = 1; i < (maxSuggestedItems - 1); i++)
    {
      ret.add(new SearchSuggestItem(searchText + " " + _NUMBERS.substring(0, i)));
    }
  
    return ret;
  }
  
  /**
   * Get top suggestions, those shown when the user clicks in the field but there
   * is not enough content to use to suggest. In this case, it will return the row 
   * and column of the clicked cell clicked.
   * @param searchText ignored.
   * @param maxSuggestedItems ignored, we only produce 1.
   * @return Suggestions to show the user.
   */
  @Override
  public List<SearchSuggestItem> getTopSuggestions(String searchText,
                                                   int maxSuggestedItems)
  {
    List<SearchSuggestItem> ret = new ArrayList<SearchSuggestItem>();
    ret.add(new SearchSuggestItem(getCell()));
  
    return ret;
  }
  
  private String getCell()
  {
    return "Cell: " + getSelectedRow() + "," + getContextCode();
  }
  
  /**
   * Use the clientId to find the InputText component, the enclosing table and
   * thereby the selected row.
   * @return selected row, -1 if cannot find for any reason.
   */
  private int getSelectedRow()
  {
    // There are 2 ways of finding the cell: one is to know which column we are
    // in by being told (I use contextCode here) and determining the row using
    // the table's selected row. However this requires the table to have
    // rowSelection="single" which is likely but not always the case.
    // The second is to use the clientId of the component and a bit of internal
    // knowledge of the encoding of the ids.
     
    int id1 = getSelectedRowUsingTable();
    int id2 = getSelectedRowUsingClientId();
    System.out.println("TableCellSuggestGroup: getSelectedRow: found row using table selected row: " + id1 +
                         ", by clientId: " + id2);
    if (id1 != id2)
    {
      if (AppsLogger.isEnabled(AppsLogger.WARNING))
      {
        AppsLogger.write(this,
                         "TableCellSuggestGroup: getSelectedRow: found differing rows, row using table selected row: " + id1 +
                         ", by clientId: " + id2,
                         AppsLogger.FINEST);
      }
    }
    // client id is more accurate across different rowSelection attribute settings.
    return id2;
  }
  
  private int getSelectedRowUsingTable()
  {
    // Method 1:
    UIComponent field = SearchUtils.findComponentByClientId(_searchFieldClientId);
  
    if (field != null)
    {
      RichTable table =
        (RichTable)PatternsUtil.getParentByType(field, RichTable.class);
  
      if (table != null)
      {
        // find the selected row.
        RowKeySet selectedRowKeys = table.getSelectedRowKeys();
        if (selectedRowKeys != null && selectedRowKeys.size() > 0)
        {
          // This works for Java ArrayLists, but will not work for BC collections.
          Integer row = (Integer)selectedRowKeys.iterator().next();
          return row;
        }
      }
    }
    return -1;
  }
  
  private int getSelectedRowUsingClientId()
  {
    // Method 2:
    String clientId = getSuggestFieldClientId();
    String[] clientIds = clientId.split(":");
    if (clientIds.length > 2)
    {
      return Integer.valueOf(clientIds[clientIds.length - 2]);
    }
    return -1;
  }
}

Example of a Prefixed Suggestion Group

package oracle.apps.fnd.searchSuggest;
  
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import oracle.apps.fnd.applcore.patterns.ui.behaviors.searchSuggest.AbstractSearchSuggestGroup;
import oracle.apps.fnd.applcore.patterns.ui.behaviors.searchSuggest.SearchSuggestItem;
  
/**
 * A search suggest group that shows a fixed set of top suggestions being the:
 * Context Code
 * Object Type
 * with which it was configured (similar to ReflectiveSearchSuggestGroup).
 * Once the user starts typing, it will show suggestions only when a prefix
 * character is typed (in this case a # character). This character will not be
 * part of the suggestions but will be returned to the field.
 * For testing.
 */
public class PrefixedSearchSuggestGroup
extends AbstractSearchSuggestGroup
{
  /**
   * Unique name of this group. Must be unique across all groups.
   */
  public static final String NAME = "prefixedterms";
  
  @Override
  public String getName()
  {
    return NAME + (getGroupIndex() + 1);
  }
  
  @Override
  public String getDisplayName()
  {
    String overRiddenDisplayName = super.getDisplayName();
    if (overRiddenDisplayName != null)
    {
      return overRiddenDisplayName;
    }
    return "Prefixed Group " + (getGroupIndex() + 1);
  }
  
  @Override
  public String getIconURL()
  {
    String overRiddenIconURL = super.getIconURL();
    if (overRiddenIconURL != null)
    {
      return overRiddenIconURL;
    }
    return "images/applcore/uishell/globalSearch/search_ena.png";
  }
  
  @Override
  public List<SearchSuggestItem> getItems(String searchText,
                                          int maxSuggestedItems)
  {
    List<SearchSuggestItem> ret = new ArrayList<SearchSuggestItem>();
    if (searchText.indexOf("#") == 0)
    {
      for (int i = 1; i <= 5; i++)
      {
        ret.add(new SearchSuggestItem(searchText.substring(1) + "suggestion" + i,
                                      searchText + i));
      }
    }
    return ret;
  }
  
  @Override
  public List<SearchSuggestItem> getTopSuggestions(String searchText,
                                                   int maxSuggestedItems)
  {
    List<SearchSuggestItem> ret = new ArrayList<SearchSuggestItem>();
     
    ret.add(new SearchSuggestItem(getContextCode()));
    ret.add(new SearchSuggestItem(getObjectType()));
     
    return ret;
  }
    
  @Override
  public void init(Map<String, Object> attributes)
  {
  }
  
  @Override
  public boolean requiresSubmit()
  {
    return false;
  }
  
  @Override
  public boolean placeSelectedItemIdInField()
  {
    return true;
  }
}

15.8 Attachments in Global Search

This section details the design and behavior of the implementation of Attachments in Global Search.

The implementation:

  • Shows attachments in the results UI using the same descriptions and tooltips that the attachments component does.

  • Allows the user to click an attachment in the results UI and have the attachment open.

  • Shows keyword bolding in the attachment name (if any).

  • Allows the developer to promote grandchildren attachments if desired. For instance, PO Line attachments are shown with the PO result.

  • Works across attachment categories.

  • Suppresses secured attachments.

  • Picks up searchable view objects with changed attachments and flags for recrawling (but not grandchildren attachments).

  • Works with the existing tag crawl functionality.

  • Does not allow uploading or deleting of attachments directly from the search UI. This must be done from the user UI that the attachments component is on. This is because attachment requires view links to the Business view objects to which they are attached. This is not available in a search result that may be displayed anywhere in Fusion Applications. However, it is probable that clicking the search result will take the user directly to this UI, so this functionality is only a click away.

15.8.1 Crawl Functionality

Crawl is done using the FndSearchPlugin that can be used either directly, or as a base class to existing plugins.

The plugin replicates the features of the TagSearchPlugin and adds Attachment functionality, as shown in the next image. Developers still can use TagSearchPlugin, but it will not do attachments. Use the FndSearchPlugin to get both sets of functionality (tags and attachments). The tag setup in the FndSearchPlugin is the same as it is in the TagSearchPlugin.

Figure 15-50 Search Plugin for Attachments

Explained in surrounding text.

There are two arguments to determine how attachments are crawled:

  • ATTACHMENT_VIEW_LINK_ACCESSOR_PATHS: A comma-separated list of accessor paths to Attachment view objects.

  • ATTACHMENT_INCREMENTAL_CRAWL_ENTITY_NAME: The name of the attachment entity used in incremental crawls to reverse lookup changed attachments.

Attribute ATTACHMENT_VIEW_LINK_ACCESSOR_PATHS

[Mandatory] This attribute defines the attachments that will be considered as part of the crawl. Without this attribute, the default attachment behavior of ECSF will be unchanged; that is, all direct attachments of the searchable view object will be crawled, but nothing will be shown in the Search UI.

It is possible to have multiple attachment view links from a master view object, and attachments stored against detail view objects in a master-detail relation.

An accessor path is a dot-separated set of accessors to an Attachment view object. The attribute ATTACHMENT_VIEW_LINK_ACCESSOR_PATHS is a comma-separated value (CSV) list of these accessor paths. For example, if GsProductAttachmentsAccessor is the view link accessor to the AttachmentVO directly under the Search view object, then this would be the accessor path to these attachments.

Similarly, if GsProductDetails is the accessor to a detail level view object that has an accessor GsProductDetailsAttachmentsAccessor to the attachments, then GsProductDetails.GsProductDetailsAttachmentsAccessor is the accessor path to these attachments.

Any number of these paths can be specified, though it is expected to be small (usually only 1).

As an example, accessors in the Master view object for this would be:

<ViewLinkAccessor
  Name="GsProductAttachmentsAccessor"
  ViewLink="oracle.apps.model.GsProductAVL"
  Type="oracle.jbo.RowIterator"
  IsUpdateable="false"/>
<ViewLinkAccessor
  Name="GsProductDetails"
  ViewLink="oracle.apps.model.GsProductToDetailsVL"
  Type="oracle.jbo.RowIterator"
  IsUpdateable="false"/>

and in the detail view object:

<ViewLinkAccessor
  Name="GsProductDetailsAttachmentsAccessor"
  ViewLink="oracle.apps.model.GsProductDetailsAVL"
  Type="oracle.jbo.RowIterator"
  IsUpdateable="false"/>

When creating master detail relations like this, do not include the accessor in the detail view object. If you do include the accessor in the detail view object, ECSF will try to follow the links from Master > Detail > Master > Detail in a recursive manner until it hits the maximum depth of 7 levels.

In such a case, you will see a crawl error similar to:

<VODocumentImpl> <assign> Deep-level view objects not supported. VO is: oracle.apps.model.GsProductDetailsVO Level: 7

Attribute ATTACHMENT_INCREMENTAL_CRAWL_ENTITY_NAME

Because adding an attachment does not change the Last_update_date of the parent view object row, there needs to be a reverse lookup mechanism used to force these additional rows onto the incremental crawl queue.

This attribute is not required. If it is not supplied, attachment additions to a view object row that is otherwise unchanged (and therefore on last_update_date change) will go un-noticed.

This attribute defines the attachment entity name that will be used when determining which searchable view object records need to be flagged for re-crawl because they have had changes to their attachments.

Note that only newly-added attachments and updated attachments (including the title) will be found for re-crawl. There is no mechanism to detect deleted attachments.

Internal Stored Attribute FND_ATTACHMENT_METADATA

Attachments found during the crawl will be recorded in the searchable view object in the predefined stored String attribute FND_ATTACHMENT_METADATA. This attribute needs to be defined in the searchable view object definition and flagged as a stored attribute. It should have no data in it because it will be over-written by the Applications Core preIndexProcessor at crawl time. An example of defining an attribute is shown below using a transient attribute.

Figure 15-51 Defining an Attribute Using a Transient Attribute

Explained in surrounding text.

What is stored in this attribute is an XML structure for all the attachments stored against a searchable object. It does not store the attachment data itself here. The attachment data is stored in the Oracle SES indexed document in an inline for making it available for text search. This example shows four attachments stored against a row:

<Attachments>
  <Attachment documentId="300100024947778" datatypeCode="FILE" title="Latte.doc" fileName="Latte.doc" />
  <Attachment documentId="300100024947776" datatypeCode="FILE" title="UIShell_dtpreview.png" fileName="UIShell_dtpreview.png" />
  <Attachment documentId="300100024881786" datatypeCode="WEB_PAGE" title="oracle" description="Oracle homepage" fileName="http://oracle.com" />
  <Attachment documentId="300100024881784" datatypeCode="TEXT" title="a quick note" description="a quick note" fileName="a quick note" />
</Attachments>

The meaning of the attributes is defined in the below table.

Having these attributes stored in the searchable view object means they are searchable by default, and the UI can use the values to quickly decode the values without costly database look-ups during the rendering of the result.

Table 15-16 Attachment Attributes

datatype Code Document ID Title Description File Name

FILE

Content server documentId

nvl(title if available, otherwise fileName)

description

fileName

WEB_PAGE

documentId

nvl(title if available, otherwise URL)

description

URL

TEXT

documentId

nvl(title if available, otherwise fileName)

description

fileName

TMURL

Not supported and there will be no attributes stored for it in SES.

nvl(title if available, otherwise URI)

description

URI (not URL)

FOLDER

Not supported and there will be no attributes stored for it in SES.

     

Mapping of AttachmentVO attributes into search metadata

Important: The XML is supplied without a schema. It is an internal part of Applications Core search and may change at any time without notice.

Attachments found at detail levels are pulled up to the parent level. If this is not required, do not include an accessor path to this level and these attachments will not be crawled.

Attachments in all categories are included. However, if that document entity has security enabled (no matter what the data security rules are), the attachments assigned to that document entity are removed from the indexed document and therefore are not crawled. Their details will not be in the XML.

The following types of attachments are available as links:

  • FILE

    An attached file is the primary use case.

  • WEB_PAGE

    A URL, such as http:www.oracle.com. The link will be placed verbatim in the results UI as an af:goLink. Note that ECSF does not crawl Web page attachments.

  • TEXT

    Text typed in by the user. This is implemented by attachments internally as a hidden file, so is practically the same as FILE.

The following types of attachments are not available as links:

  • FOLDER

    A reference to a folder in UCM. It requires use of UCM folder taskflow.

  • TMURI

    Topology Manager URI. This is a URI offset to a web application defined in topology. This is a minor use case used in migration and is not supported by search.

Attachment documentId compared to Attached Document Id

documentId is a globally-unique number identifying an attachment. It will return the latest version of the attachment.

Attached DocumentId is a globally-unique number identifying a specific version of an attachment. It will return a specific version of the attachment.

As an example, documentId 100000000001 is for file BRD.doc. It has Attached DocumentId 100000000002 for the initial version of the file, and 100000000003 for the next version of the document.

Global Search will use the documentId and always download the latest version of the file.

15.8.2 Show Attachments

Attachments will be shown on a line below the "Other Actions" in the Search Result. That is the lowest part of a result item.

The text of the link will be the Attachment title, which generally defaults to the filename in the Attachments UI, but may be overridden by the user when the attachment is uploaded.

The tool tip of the attachment will be the attachment description entered by the user. If this is not present, the title value will be used.

Attachments will be sorted alphabetically.

All information is available in the stored attribute of the Search view object to render attachment links in the UI with no additional lookup work, and therefore there is no significant performance overhead.

This image shows documents attached to a search result and the tooltip.

Figure 15-52 Documents Attached to a Search Result

Explained in surrounding text.

15.8.3 Open Attachments

Clicking an attachment link, shown in this figure, will open the content for the user. The browser settings should determine how this is done and should be in line with what the Attachment component does.

Figure 15-53 Open Attachment Dialog

Explained in surrounding text.

For example, clicking a JPG file attachment link usually will open it directly in a browser tab, while clicking a .doc file link usually will open it in Microsoft Word.

15.8.4 Implement the Attachment Plugin

There are two cases to consider:

  • The searchable view object has no plugin currently configured: Simply use the Applications Core plugin.

  • The searchable view object has a plugin already: Set up the existing plugin to inherit the Applications Core plugin.

15.8.4.1 Use the Applications Core Plugin

To use the existing Applications Core plugin, follow these steps:

  1. In the ECSF Search view object design time, add a Search Plugin to the Applications Core class oracle.apps.fnd.applcore.search.plugin.FndSearchPlugin.
  2. Configure attachments that will be crawled.

    Add a parameter with a comma-separated list of accessor paths with key ATTACHMENT_VIEW_LINK_ACCESSOR_PATHS.

  3. Configure searchable view object incremental crawl detection of searchable view objects with new attachments.

    Optionally, add an attribute with key INCREMENTAL_CRAWL_ATTACHMENT_ENTITY_NAME and value of the Attachment Entity Name (the value you enter in the Attachment View Link wizard, such as GS_PRODUCTS).

    This will add searchable view objects to the crawl list when new attachments are added. This is useful as the searchable view object last_update_date value is not updated when an attachment is added, and the document will not be detected as needing a re-crawl. This will flag the individual searchable view object row to be recrawled. Note that it is not possible to detect changed attachments this way at the detail level (for example a purchase order line's attachments), because the reverse lookup would obtain the primary key of the line item and not the searchable view object.

15.8.4.2 Enhance Existing Plugin

If you have your own plugin, extend your plugin using the following steps:

  1. Extend the Applications Core plugin and call super() on the methods that implement attachment functionality. Use the same parameters to the plugin as described above.
    public class AppsSearchPlugin
      extends FndSearchPlugin
      implements PreIndexProcessor,
                 ChangeListener
    {
      /**
       * Hook into the pre index process of the crawl so we can annotate the SVO
       * with apps specific information such as tags and attachment metadata.
       * @param ctx Search Context.
       * @param docs Documents to iterate and find tag children for.
       */
      public void preIndexProcess(SearchContext ctx,
                                  List<IndexableDocument> docs)
    {
      super.preIndexProcess(ctx, docs);
      // custom apps functionality ...
     
    }
        /**
       * Get the change list for crawling.
       * @param ctx Search Context.
       * @param changeType the type of the change requested. Must be one of following
       * <ul>
       * <li>ChangeListener.UPDATE<li>
       * <li>ChangeListener.INSERT<li>
       * <li>ChangeListener.DELETE<li>
       * </ul>
       * @return an iterator of a list of primaryKeys. Returns an empty iterator if
       * nothing changed.
       */
      public Iterator getChangeList(SearchContext ctx, String changeType)
      {
         Iterator ret = super.getChangeList(ctx, changeType);
        // custom apps functionality ...
        return ret;
      }
    }
    
  2. Add the transient stored attribute FND_ATTACHMENT_METADATA.

    Add a transient String attribute to the searchable view object named FND_ATTACHMENT_METADATA to store Applications Core attribute metadata. Flag this attribute as stored in the searchable view object definition.

15.9 Support Global Search Alternate Words for the Cloud

To support Cloud customers, a Fusion Applications user interface allows upload and download of alternate search words to Oracle Secure Enterprise Search.

Oracle SES is the standalone database and Oracle WebLogic Server applications server used for search. It provides a store of alternate words and web service endpoints for searching and administration.

Overview

A new taskflow provides maintenance of Oracle SES alternate words.

It uses APIs to determine the Oracle SES Administration service endpoint, username and password, and uses the Oracle SES web service stubs available in the Oracle Enterprise Crawl and Search Frameworkshared library.

The setup taskflow is available in the GlobalSearch.jws application.

FndSetup will host the taskflow, as it does for all setup flows. It already has the "ECSF Runtime Server" library in the weblogic-application.xml file:

<library-ref>
  <library-name>oracle.ecsf</library-name>
</library-ref>

The taskflow will talk to Oracle SES directly, instead of to ECSF search that federates through search servers that talk to Oracle SES. There is no need for federation because there is only one Oracle SES for Fusion Applications.

The setup flow dialog appears similar to this example:

Figure 15-54 Example of the Alternate Words Maintenance Taskflow Dialog

An example of the Alternate Words Maintenance Taskflow Dialog.

The taskflow has one parameter:

Name Optional Description
pageTitle Yes Title to be shown at the top of the page. The default is "Manage Alternate Words"

The dialog uses these buttons:

  • Add - Adda new row above the selected one (or row 0 if none are selected).

  • Duplicate - Duplicate the existing row.

  • Edit - Edit the current row (all fields).

  • Delete - Delete the current row.

Note that a row is identified by the "User Input Keyword" and "Alternate Keyword."

A user can edit any field on the row, including the two key fields. This will delete the old record and add the new one.

15.9.1 Look Up the SES Endpoint (ECSF APIs)

ECSF/SES does not use the connections architecture (connections.xml) to determine SES endpoints, which are saved in ECSF tables.

This code, which mimics ECSF directly, is used to look up the endpoint. Because there is only one SES per Fusion Applications instance, the first configured endpoint will be used.

private MetaEngineInstance getActiveLocalInstance()
{
  MetaEngineInstance ret = null;
  List<MetaEngineInstance> allECSFComponents = MetaDataManager.getEngineInstances(3);

  for (MetaEngineInstance localInstance : allECSFComponents)
   {
     Map params = localInstance.getParameters();
     String sesAdminUsername = (String)params.get("SES_ADMIN_USERNAME");
     String sesAdminService = (String)params.get("SES_ADMIN_SERVICE");
     String isActive = (String)params.get("IS_ACTIVE");
 
     if (sesAdminUsername != null && sesAdminUsername.indexOf("[CHANGE_ME]") == -1
        && sesAdminService != null && sesAdminService.startsWith("http"))
     {
       ret = localInstance;
       break;
     }
   }
   return ret;
}

The endpoint, username and password will be used to create the Administration port connection (see below).

If no endpoint can be found, an error will be shown - see the error messages section below for details on the error messages that may be shown.

Figure 15-55 Endpoint Not Found Message

A screenshot of an Endpoint Not Found error message, explaining that Secure Enterprise Search was not configured in the environment.

15.9.2 SES Web Service Calls

There are four web service calls used by Global Search Alternate Words for the Cloud.

Administration Port Connection

private AdminPortType getStatefulWebServiceClient(
  String webServiceURL,
  String userName,
  String password) throws Exception
{
  AdminService adminService = new AdminService(
    new URL( webServiceURL ),
    new QName(
      "http://search.oracle.com/Admin",
      "AdminService"
    )
  );
  
  AdminPortType adminPort = adminService.getAdmin();
  
  // Tell client proxy to maintain HTTP session for stateful behavior
  ((BindingProvider)adminPort).getRequestContext().put(
    BindingProvider.SESSION_MAINTAIN_PROPERTY, true
  );
  
  // Create credentials argument
  Credentials credentials = new Credentials();
  credentials.setUserName( userName );
  credentials.setPassword( password );
  
  adminPort.login( credentials, getLanguage() );
  
  return adminPort;
}

Alternate Word Retrieval

This example shows how to retrieve an alternate word.

private List<AlternateWord> getAltWords(AdminPortType adminPort)
throws Exception
{
  ObjectOutput oo = adminPort.exportAll(
            "altWord",
            null,
            null, // "password",
            null, // credentials,
            null,
            getLanguage()
          );
   
  return toAltWords(oo.getObjectXML());
}

Alternate Word Deletion

This example shows how to delete an alternate word.

private void removeAltWords(AdminPortType adminPort,
                                 List<AlternateWord> altWords)
  throws Exception
  {   
    List<ObjectKey> objectKeys = new ArrayList<ObjectKey>();
    for (AlternateWord word : altWords)
    {
      // Need to use the original key values in case the user has updated, then
      // deleted the row.  Note that do not need to worry about new rows as these
      // will never be in the deleted list.
      objectKeys.add(createKeyPairs(word.getOrigKeyword(), word.getOrigAltWord()));
    }
     
    List<OperationControl> ops = toOperationControlList("IGNORE_NOT_FOUND", "true");
     
    adminPort.deleteList(
      "altWord",
      objectKeys,
      null, // credentials,
      ops,
      getLanguage()
    );
  }

Alternate Word Insertion

This example shows how to insert an alternate word.

private void createAltWords(AdminPortType adminPort,
                            List<AlternateWord> altWords)
throws Exception
{
 
  StringBuilder sb = new StringBuilder(
  ">?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
  ">search:config productVersion=\"11.1.2.0.0\" xmlns:search=\"http://xmlns.oracle.com/search\">" +
  "<  search:altWords>");
    for (AlternateWord word : altWords)
    {
      sb.append(toAltWordXML(word.getKeyword(), word.getAltWord(), word.isAutoExpand()));
    }
  sb.append("  /search:altWords>" +
  "</search:config>");
   
  List<OperationControl> ops = toOperationControlList("DUPE_METHOD", "overwrite");
   
  adminPort.createAll(
    "altWord",
    sb.toString(),
    null, //"password",
    null, // credentials,
    null,
    ops,
    getLanguage()
  );
}

15.9.3 Alternate Words Error Messages

This table shows error messages that may be shown to the user for setup or web service communications issues.

Message Where Shown Meaning

Secure Enterprise Search is not configured in this environment.

When retrieving the list of alternate words when the taskflow starts.

The ECSF metadata that configures SES is not available or is not complete.  The SES web service endpoint cannot be determined.  It is likely Global Search is also not available.

Secure Enterprise Search is not responding.

When retrieving the list of alternate words when the taskflow starts, or saving after the "Save and Close" button is pressed.

The SES endpoint cannot be contacted.

Cannot retrieve the list of Alternate Words from Secure Enterprise Search.

When retrieving the list of alternate words when the taskflow starts

The SES endpoint cannot be contacted, but the list of alternate words cannot be retrieved from it.

The following Alternate Words already exist for the User Keyword. Delete the duplicates:

When saving alternate words.

Within the list of alternate words there are duplicate keyword/altword pairs.

15.10 Implement List Search

List Search, sometimes known as "local search," is for implementing search functions on a single data source (also known as a View Object).

It comprises a keyword search box, autosuggest recent items and saved searches, and a collapsible Filters panel that uses ADF Query. Use the full List Search component when users need keyword search augmented by fielded search and saved searches. This image shows an overview of List Search.

Figure 15-56 Overview of List Search Components

A graphical overview of the different List Search components.

A new declarative component supports List Search. It is available from the Applications Core component gallery and from the Data Control palette for drag-and-drop of View Criteria to the page.

A running List Search would resemble this image.

Figure 15-57 List Search Declarative Component with Suggestions

A screenshot showing the List Search Declarative Component with suggestions.

This image shows:

  • The omnibox is labelled "Search" with shadow text.

  • A set of suggestions (note the UI is pre-Rel 10 so has changed a bit),

  • Recent items from the existing recentitems suggest group,

  • Three saved searches - "All People", "Analyst" and "ElizabethDirects."

CSS Styles

Style Description

FndListSearch

Style on the root-most af:panelStretchLayout that makes up fnd:listSearch

FndListSearchQuery

Style set on the af:query within fnd:listSearch

FndListSearchClearLink

Style set on the clear link (the 'X') on the right of the search field

Declarative Components

Attribute Name Evaluated Type Values Default Required Description

id

String

 

n/a

Yes

Generated for DC automatically by ADF.

rendered

boolean

true false

true

No

Generated for DC automatically by ADF.

binding

RichDeclarativeComponent

n/a 

n/a

No

Generated for DC automatically by ADF.

keywordSavedSearch

String

n/a 

n/a

Yes

Name of the search used for keyword searches.  Must correspond to a View Criteria name (the code name, not the translated name).  This View Criteria will be run when the user types in free-form search terms.

keywordSearchField

String

n/a 

n/a

Yes

Name of the field in the keywordSavedSearch to place the search text when performing a keyword search.  Must correspond to a text field in the keywordSavedSearch View Criteria whose default value is not set.  This field will be assigned the free-form search terms entered by the user, and should be at least 30 characters in length.

suggestGroups

java.lang.List

n/a 

null

No

The suggest groups to show in the omnibox. This is a List of SearchSuggestGroupOption and will be used instead of suggestGroup1 .. 8 and the contextCode and objectTypes if available.  Some implementations may find it easier to define the groups in a managed bean this way.

suggestGroup1 .. 8

String

n/a

null

No

Individual suggestion Groups to be shown in the order defined.

contextCode1 .. 8

String

 n/a

null

No

Context codes for the appropriately numbered group.

objectType1 .. 8

String

n/a 

null

No

Object types for the appropriately numbered group.

queryBindingId

String

n/a 

n/a

Yes

The id of the af:query search region, for example in the below pageDef entry it would be "AllAttributesQuery."

<searchRegion Criteria="AllAttributes"
        Customizer="..."
        Binds="PeopleIterator"
        id="AllAttributesQuery"/>

resultBindingId

String

 n/a

n/a

Yes

The id of the results binding, for example in the below pageDef entry is would be "GsEmp1". This is used to get the estimated result count to update the keywords list.

<bindings>
<tree IterBinding="GsEmp1Iterator" id="GsEmp1"
         <nodeDefinition DefName="applcore....GsEmpVO"
                Name="GsEmp10"
                <AttrNames>
                <Item Value="Empno"/>
                <Item Value="Ename"/>
                ...

queryListener

javax.Expression Language.MethodExpression

n/a 

null

Yes

Required if you wish to customize the query criteria - see http://www.oracle.com/technetwork/developer-tools/adf/learnmore/85-querycomponent-fieldvalidation-427197.pdf

Bound to the ADF queryListener value in the pageDef, for example: #{bindings.AllAttributesQuery.processQuery}

resultComponentId

String

n/a 

n/a

No

Id of result for af:query to ppr.

searchFieldLabel

String

 n/a

"Search"

N/a 

Label of the search field.

searchFieldPlaceholder

String

 n/a

null

No

The shadow text (placeholder text) of the Search field.

searchFieldTextSelection

String

default /

click

default

No

How is text selection handled in the field. Options are 'default' which will use the inputText field default selection which is typically selection of all text on tab into the field (likely browser dependent) and 'click' which will select all text in the field when the field is first clicked in with the mouse.

suggestGroupHeadings

String

on/off

on

No

Whether group heading are shown in the search field autosuggest.

suggestGroupIcons

String

on/off

off

No

Whether group icons are shown in the search field autosuggest.

suggestNoMatchMessage

String

n/a 

null

No

The message shown when there is no match to the user's suggestions.

suggestTopSuggestions

String

on/off

on

No

Whether top suggestions are shown when the user first clicks in the field.

suggestMinCharCount

Integer

n/a 

1

No

The minimum number of characters before suggestions are shown. Before this number top suggestions may be shown.

suggestMaxSuggestedItems

Integer

n/a 

15

No

The maximum number of suggestions shown to the user.

suggestSubmitKeywordsOnSelection

Boolean

n/a 

true

No

Does the selection of a keyword from the suggestion list also submit a search?

splitterPosition

Integer

n/a 

300

No

The default splitter position.

splitterCollapsed

Boolean

true / false

true

No

The default splitter collapse state.

Facets

Name Description

results

Results area containing Design Time-generated output and modifiable by the developer

15.10.1 How to Implement List Search at Design Time

The developer develops View Criteria using normal BC DT. The default BC View Object View Criteria UI in JDeveloper can be used, although many options need to be disabled or simplified.

View Criteria

There should be one View Criteria that will be later designated the "Default Saved Search." This saved search is run when the fnd:listSearch component is first run. It is important that this criteria is well tuned and runs quickly because it directly affects the initial rendering of the fnd:listSearch component.

There should be another saved search that later will be designated the "Keyword Saved Search." This saved search is used whenever the user enters keywords and clicks Search. It must have a Text field of at least 30 characters in width that is tuned to provide fast query responses (30 characters is the width of the search field). If the field is shorter than this, the keywords will be truncated.

Developers should not use only one View Criteria to perform as both the default and keyword saved searches.

Once generated, these saved searches will display in the JDeveloper Data Controls Palette as "Named Criteria," also known to af:query as system saved searches.

The development of these two View Criteria is mandatory before the fnd:listSearch component can be configured.

An example Keyword Saved Search View Criteria is shown below using the BC View Object UI in JDeveloper.

Figure 15-58 Example Keyword Saved Search View Criteria

A screenshot of the Edit View Criteria saved search window with “keywordSS” as the Critera Name being searched.

Click the Criteria Definition tab.

Figure 15-59 Criteria Definition Tab

A screenshot showing the Criteria Definition Tab of the Edit View Criteria window.

The Criteria Name (not the Display Name) is used as the keywordSavedSearch attribute later in the fnd:listSearch component.

Click the Criteria UI Hints tab, then the Custom Operators tab.

Figure 15-60 Custom Operators Tab

A screenshot showing the Custom Operators tab of the Edit View Criteria window.

Turn off Custom Operators.

Component

Developers will drag and drop the VO View Criteria they wish to be the Design Time default saved search (Data First DT).The developer will drag and drop the listView component from the Component Palette (Component First DT).

The user will be able to change the default saved search later by personalizing the saved searches and selecting another default, but this will be the initial default for all users.

The Create List Search wizard starts and displays the Specify data source and criteria dialog.

Figure 15-61 Data Source and Criteria

A screenshot of the Specify Data Source and Criteria for List Source dialog.

When dragged-and-dropped from the Data Controls Palette, this is the View Criteria that is dropped.

The user must select a default saved search to continue. The "All Queriable Attributes" saved search produced by ADF cannot be selected.

Click Next to specify the Keyword Saved Search.

Figure 15-62 Keyword Saved Search

A screenshot showing the Keyword Saved Search dialog.

Select the keyword saved search from the list of all View Criteria, and the keyword field from this View Criteria.

The "All Queriable Attributes" criteria produced by ADF will not be available for selection.

Click Next to specify the Search Field Properties.

Figure 15-63 Search Field Properties

A screenshot showing the Search Field Properties dialog.

Set the Search Field Label and Search Field Placeholder.

Select Suggestion Groups and their contextCode and objectTypes. Three suggestion groups will be available:

  • Search Terms - A new suggest group for keywords, and a contextCode/ObjectType to save these under.

  • The contextCode and objectType will allow the keywords to be specific to the list search view object.

  • Recent items - The existing recent items suggest group with a contextCode/ObjectType .

  • List Saved Searches - The list search names, this will be the same values seen in the af:query saved search drop down.

Click Next to specify the type of results.

Figure 15-64 Select Results Type

A screenshot showing the Select Results Type dialog.

The developer can select from a number of result types:

  • Card and List View - This will invoke the Card and List Layout wizard.

  • Applications Table - This will invoke the Applications Table wizard.

  • ADF List View - panelFormLayout - This will allow results in a listView using a panelFormLayout to format the results in the List View row.

  • ADF List View - Grid Templates - Allow the use of the Quick Grid wizard to configure data in a listView.

  • ADF List View - Detailed Grid - Allow the use of the Detail Grid wizard to configure data in a listView.

  • ADF Table - Use an af:table as the results.

This selection determines the rest of the steps except for the final (summary) step.

Results Card and Listview

This image shows the first of the four wizard dialogs used when the Card and Listview option was selected in the previous dialog.

A screenshot of the first of the four wizard dialogs used when the Card and Listview option was selected.
Applications Table

If Applications Table was selected, another three wizard dialogs are displayed. See Adding Applications Table Components Using the Applications Table Wizard for descriptions of the Set Row Selection and Layout Columns, the Select the table CRUD patterns, and the Set Table Features dialogs.

After these dialogs, click Next to display the Summary. This example shows the Summary information for the Card and Listview selection. The information for the Application Table selection is almost identical.

The summary information for the Card and Listview selection.

Click Finish to create the code, or Back to make changes.

These images show examples of the code that is created.

An example of generated code
An example of generated code.