This chapter includes the following sections:
For more information about the features, see:
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.
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).
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.
Crawling is done using the FndSearchPlugin
that can be referenced by developers, either directly or as a base class to existing plugins.
The TagSearchPlugin
is deprecated and stubbed with the removal of Webcenter tags. Developers should switch to FndSearchPlugin
.
Figure 15-1 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
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. |
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.
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
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
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.
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.
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.
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; } }
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.
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 ).
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
.
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)
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.
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.
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
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
The parent task flow named ToParentSFFlow
is shown in Figure 15-7.
Figure 15-7 Example Parent Task Flow
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
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.
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.
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.
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
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.
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.
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 |
---|---|---|---|
|
VARCHAR2(100) |
Yes |
Primary Key - Unique code based on Product Code Prefix. Ensure that this code begins with |
|
VARCHAR2(30) |
Yes |
Reference to Product teams will seed the lookup type with meaning for the category ( |
|
VARCHAR2(4000) |
Yes |
Reference to |
|
VARCHAR2(32) |
Yes |
Reference to |
|
NUMBER |
Yes |
Reference to |
|
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. |
|
VARCHAR2(400) |
Yes |
The service that must be invoked for count calculation (for refreshing this category). |
|
VARCHAR2(400) |
Yes |
Obsolete. The hard-coded method name will be |
|
VARCHAR2(1) |
Yes |
Defaults to Y. Defines if this Watchlist category is enabled or active. |
|
VARCHAR2(64) |
Yes |
Standard WHO Column |
|
TIMESTAMP |
Yes |
Standard WHO Column |
|
VARCHAR2(64) |
Yes |
Standard WHO Column |
|
TIMESTAMP |
Yes |
Standard WHO Column |
|
VARCHAR2(32) |
Yes |
Standard WHO Column |
Table 15-3 ATK_WATCHLIST_SETUP
Column Name | Datatype | Required | Comments |
---|---|---|---|
|
VARCHAR2(100) |
Yes |
Primary Key - Uses the Category Code Prefix |
|
VARCHAR2(30) |
Yes if |
Reference to 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. |
|
VARCHAR2(30) |
Yes |
None |
|
VARCHAR2(1) |
Yes |
Identification of a Watchlist item that is created against a security action instead of a specific user. |
|
Yes if
|
Defines if this item is created against a security action. Users that have this action will be able to view this item. |
|
|
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 |
|
|
VARCHAR2(30) |
Yes |
Defines the Watchlist item type and maps to the lookup. Valid values are:
|
|
VARCHAR2(200) |
Yes if |
Human Task Definition Identifier |
|
VARCHAR2(100) |
Yes if |
Human Task State Identifier. |
|
NUMBER(9) |
Yes if |
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 |
|
VARCHAR2(400) |
Yes if |
Complete path of the view object that must be executed for count calculation. |
|
VARCHAR2(400) |
Yes if |
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. |
|
VARCHAR2(400) |
Yes if |
Complete path of the application module that contains the view object instance that must be executed for count calculation. |
|
VARCHAR2(400) |
Yes if |
The application module configuration name for creating an instance of the application module from code. This is typically |
|
VARCHAR2(400) |
Yes if |
The instance name for the view object in the application module. |
|
VARCHAR2(400) |
Yes if |
The view criteria that must be applied when executing the view object. |
|
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. |
|
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. |
|
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. |
|
VARCHAR2(400) |
Yes |
The task flow for this Watchlist item's drilldown from the Watchlist UI. |
|
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, |
|
VARCHAR2(400) |
Yes if |
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, For a user-created saved search, the view criteria ID will be appended to this string (the string must end with |
|
VARCHAR2(400) |
Yes |
Label for the task flow to open in the target workspace. |
|
VARCHAR2(1) |
Yes |
Defaults to Y. A definition that indicates if this Watchlist category is enabled or active. |
|
VARCHAR2(64) |
Yes |
Standard WHO Column |
|
TIMESTAMP |
Yes |
Standard WHO Column |
|
VARCHAR2(64) |
Yes |
Standard WHO Column |
|
TIMESTAMP |
Yes |
Standard WHO Column |
|
VARCHAR2(32) |
Yes |
Standard WHO Column |
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
)
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).
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
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;
Follow the procedures described in this section to use the Watchlist.
To ensure the Watchlist link works in your pages, complete these steps.
See Watchlist Physical Data Model Entities for details of the entire Watchlist data model. Seed only ATK_WATCHLIST_CATEGORIES
and ATK_WATCHLIST_SETUP
.
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.
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 |
---|---|
|
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.
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:
Refer to Implementing the Watchlist.
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.
Your Service project must import the other JAR files from your product that must be used by the Watchlist code.
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.
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); }
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.
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; }
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.
Ensure the AdfAtkWathcListPublicUi.jar
file is available (usually in the fusionapps/jlib
directory).
Start JDeveloper and open the .jspx or .jsff page containing the query region whose saved searches have to be promoted.
In the query region, add a toolbar facet.
Right-click the component.
In the menu that opens, select Facets-Query.
From the submenu, select Toolbar, as shown inFigure 15-11.
Figure 15-11 Adding Toolbar Facet to Query Region
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
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>
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
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
When the ADF Task Flows node is expanded, you should see two task flows. The task flow AtkWatchlistUserSavedSearchPromotionTF
is the one to be used.
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:
Select the userSavedSearchList input field, click the small "v" icon present at the end of the field and select the Expression Builder option.
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}
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
Click OK. This creates a region component in the UI page, as shown in Figure 15-16.
Figure 15-16 Created Region Component
Open the page definition file and select the executable associated with the Watchlist-related task flow.
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
Add a task flow security permission to the AtkWatchlistUserSavedSearchPromotionTF
task flow in the jazn-data.xml
file.
Open the jazn-data.xml
file and select the ADF Policies tab.
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
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
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
Additional Steps for Existing Consumers
If you already have implemented promoting Saved Search to the ATK Watchlist, there are four additional steps.
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}"/>
Use the public List<String> getWatchListUserSavedSearchList()
Java method by passing the QueryModel binding of your af:query
.
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}"
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; }
If you have seeded the information properly, your normal task flows should work when expanded into from the Watchlist portlet.
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(); }
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
.
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
.
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.
Follow these steps to implement Group Spaces.
Ensure the Oracle WebCenter Portal Spaces Client library, spaces-webservice-client.jar
, has been added to the Project.
Define an application connection to point to the URL of the WebCenter Portal Spaces WebService. To do this:
Right-click Connections in the Application Resource palette.
Select New Connection > URL.
Enter the value $HOST:$PORT
/webcenter/SpacesWebService
, where $HOST and $PORT are the hostname and the port on which the spaces application is running.
Save this connection with the name SpacesWebServiceEndpoint.
To make a homepage tab appear within UI Shell, deploy the Functional Setup Manager application.
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.
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.
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.
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}
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.
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();
This section provides details about the steps involved in integrating this feature with Oracle Fusion Applications.
To define a business event, follow these steps:
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>
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) |
---|---|---|---|---|
|
boolean |
By default the base class implementation returns |
Required |
None |
|
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 |
|
|
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 |
Optional |
|
|
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 |
|
|
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 |
Optional |
|
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 |
---|---|---|---|
|
String |
This attribute's value is used to identify the business object service to be associated with the Activity. |
|
|
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 |
|
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 |
|
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 |
|
|
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 |
|
|
String |
This attribute should provide a comma-separated list of object-type names associated with a particular Activity. Programmatically 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 The order of the objects in this array can be used to reference the objects in the Activity message format string. |
Emp, Dept |
Defining Activities requires:
Adding the ActivityStream UI task flow
Defining Activities in the service-definition.xml
file
To add the ActivityStream task flow:
taskFlow
resourceId
parameter to #{securityContext.userName}
. This will tie the task flow to the current user at runtime.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.
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.
In the Edit dialog, select User Metadata and click Add. Browse to the metadata directory that was just added and click OK.
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">
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>
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:
Summarize activities by finding a common object referenced in the Activity.
Summarize or aggregate the Actors either by listing them if there are three or fewer, or by counting them if there more than three.
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:
James updated Project Alpha tasks.
Viju updated Project Alpha tasks.
Ling updated Project Alpha tasks.
Monty updated Oppty 200 laptops status
Monty updated Oppty Solaris workstations status
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"}
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>
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 |
---|---|
|
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 |
|
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. |
|
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>
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>
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.
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"); } }
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>
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>
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>
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.
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.
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
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
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
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
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
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.
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
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
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
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
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
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.
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
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
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 ( |
GlobalSearch-View.jar |
ViewController |
Applications Core (Setup UI) ( |
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. |
To access the setup flows in Fusion Applications:
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.
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
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.
To edit a global search configuration, follow these steps:
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
To manage a Suggestion Group, follow these steps:
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:
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%
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:
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
Figure 15-42 shows the attributes for Searchable view object lookup types.
Figure 15-42 Tags - Lookup Types
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
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:
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.
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
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
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.
Figure 15-47 shows a URL Action.
Figure 15-47 Search Result Actions - URL Action
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 |
---|---|---|
|
No |
URL of icon to show next to the Action. Can be a relative reference such as |
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.
Figure 15-48 shows a Task Action.
Figure 15-48 Search Result Actions - Task Action
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 |
---|---|---|
|
Y |
Name of the page. This is shown in the browser URL bar. For example, in |
|
N |
Parameters list for the page. This is a semicolon delimited String of name-value pairs. For example, |
|
Y |
Name of the task definition file. For example, |
|
Y |
The task flow definition ID. Available from the task definition file <task-flow-definition> ID attribute. For example, |
|
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 |
|
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 |
|
N |
URL of an icon to show next to the Action. Can be a relative reference such as |
|
N |
Tooltip of the action. This also is available for URL actions. |
|
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). |
|
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. |
|
N |
Used for Recent Items striping of data by business line. This attribute will divide recent items into groups. Valid values are:
|
|
N |
Used for Recent Items striping of data by business line. This is a further subdivision within the |
|
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 |
|
N Note: Required only if the user is to be directed to a a FUSE result page. |
When navigating to FUSE, this is the If this parameter is specified, 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. |
|
N Note: Required only if the user is to be directed to a a FUSE result page. |
When navigating to FUSE, this is the 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. |
|
N Note: Required only if the user is to be directed to a a FUSE result page. |
When navigating to FUSE, this is the 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. |
|
N (conditionally) |
The /WEB-INF/oracle/apps/fnd/applcore/patterns/uishell/GlobalSearchDummyResultsTaskFlow.xml#GlobalSearchDummyResultsTaskFlow |
|
N |
The task keylist used to identify dynamic tabs. |
|
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.
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.
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 |
---|---|---|---|
|
N |
Y |
(This attribute is used for pages.) Check security of the page against the policies that are located in LDAP. The |
|
N |
Y |
A delimited string of page definition names. If this parameter is supplied, the |
|
Y |
Y |
Name of the page for the pillar. This is shown in the browser URL bar. For example, in |
|
Y |
Y |
Attribute used to look up the host and port of the associated Work Area or Dashboard from the |
|
N |
Y |
Parameters list for the page. This is a semicolon delimited string of name-value pairs. For example, |
|
Y |
Y |
Name of the task definition file. For example, |
|
Y |
Y |
The task flow definition ID that is available from the task definition file |
|
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 |
|
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 |
|
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: |
|
N |
N |
The URL of the icon to show next to the Action.It can be a relative reference, such as |
|
N |
N |
A tooltip of action. This also is available for URL actions. |
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>
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.
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.
Oracle Fusion Applications Search can also be used with non-standard ECSF searchable view objects.
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.
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.
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
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 |
---|---|---|---|---|
|
Yes |
No |
Popup |
The type of search suggest behavior. Valid values are |
|
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. |
|
Yes, resolves to an integer |
No |
3 |
The minimum number of characters that need to be typed before the suggestions are shown. |
Note: |
Yes, resolves to a |
Yes for No for |
N/A |
A value that resolves to an 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:
These predefined groups are only available to use within UI Shell pages. |
Note: |
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.)
|
Note: |
Yes |
No |
null |
A further division under |
|
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. |
|
Yes, resolves to an integer |
No |
500 |
The delay between keystrokes before the suggestions are fetched. The number is a time in milliseconds. |
|
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
|
|
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. |
|
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 This component needs to be a container component; |
|
Yes, resolves to a string |
No |
null |
A relative Component ID representing a component to hide if This component needs to have |
|
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 |
|
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 |
|
No |
The relative component Id of an Oracle ADF popup in which inline suggestions are to be shown. This popup must have The Oracle ADF popup should have a panel with Id corresponding to This attribute is only relevant when |
||
|
No |
A relative Component Id of a component on which to set a "search for 'linda'" message. This attribute is only relevant when |
||
|
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 |
||
|
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. |
||
|
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. 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'. |
||
|
No |
This attribute is only relevant when The relative component Id of an ADF popup in which inline suggestions are to be shown. This popup must have The ADF popup should have a panel with Id corresponding to |
||
|
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. |
||
|
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 |
||
|
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.
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>
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); } }
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.
Dynamic UI support for searchSuggestBehavior
satisfies these three requirements:
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.
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
.
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; } }
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.
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
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
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.
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
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
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.
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.
To use the existing Applications Core plugin, follow these steps:
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
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.
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
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() ); }
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. |
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 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
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 |
---|---|---|---|---|---|
|
String |
|
n/a |
Yes |
Generated for DC automatically by ADF. |
|
boolean |
true false |
true |
No |
Generated for DC automatically by ADF. |
|
RichDeclarativeComponent |
n/a |
n/a |
No |
Generated for DC automatically by ADF. |
|
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. |
|
String |
n/a |
n/a |
Yes |
Name of the field in the |
|
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 |
|
String |
n/a |
null |
No |
Individual suggestion Groups to be shown in the order defined. |
|
String |
n/a |
null |
No |
Context codes for the appropriately numbered group. |
|
String |
n/a |
null |
No |
Object types for the appropriately numbered group. |
|
String |
n/a |
n/a |
Yes |
The id of the <searchRegion Criteria="AllAttributes" Customizer="..." Binds="PeopleIterator" id="AllAttributesQuery"/> |
|
String |
n/a |
n/a |
Yes |
The id of the results binding, for example in the below <bindings> <tree IterBinding="GsEmp1Iterator" id="GsEmp1" <nodeDefinition DefName="applcore....GsEmpVO" Name="GsEmp10" <AttrNames> <Item Value="Empno"/> <Item Value="Ename"/> ... |
|
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 |
|
String |
n/a |
n/a |
No |
Id of result for |
|
String |
n/a |
"Search" |
N/a |
Label of the search field. |
|
String |
n/a |
null |
No |
The shadow text (placeholder text) of the Search field. |
|
String |
default / click |
default |
No |
How is text selection handled in the field. Options are 'default' which will use the |
|
String |
on/off |
on |
No |
Whether group heading are shown in the search field |
|
String |
on/off |
off |
No |
Whether group icons are shown in the search field |
|
String |
n/a |
null |
No |
The message shown when there is no match to the user's suggestions. |
|
String |
on/off |
on |
No |
Whether top suggestions are shown when the user first clicks in the field. |
|
Integer |
n/a |
1 |
No |
The minimum number of characters before suggestions are shown. Before this number top suggestions may be shown. |
|
Integer |
n/a |
15 |
No |
The maximum number of suggestions shown to the user. |
|
Boolean |
n/a |
true |
No |
Does the selection of a keyword from the suggestion list also submit a search? |
|
Integer |
n/a |
300 |
No |
The default splitter position. |
|
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 |
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
Click the Criteria Definition tab.
Figure 15-59 Criteria Definition Tab
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
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 thelistView
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
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
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
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
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.
This image shows the first of the four wizard dialogs used when the Card and Listview option was selected in the previous dialog.
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.
Click Finish to create the code, or Back to make changes.
These images show examples of the code that is created.