Monday, December 28, 2009

Scrolling Table :Table with horizontal Scrollbars and vertical scrollbars

Hi All,
I hope this is quite an interesting reading for most developers and they will try to put this feature in OAF tables.This is a very essential requirement in any of the custom OAF pages, where you show data in OATableBean and table has lot of columns, so, user has to scroll the entire page from scroll bar which is automatically rendered by browser if table width is more than page width.This is very painful for user as everytime, he scrolls the scroll-bar to see extra table columns,entire page is scrolled and he has to scroll back to the page to see other data.Similary, if you are displaying more than 10-20 rows in the table , user needs to scroll the entire page to see all the rendered rows of table.

In order to lessen the pain of the user and to have better UI design, we will render scroll bars both horizontal and vertical just surrounding the table
as , shown in the image below :


So, now lets focus how can we bring these scroll bars. In that case you can use the DIV tag to do the job for you.Put 2 RawText Bean(named DivStart, DivEnd), before and after the Table Bean in the JRAD page as shown in the image below IN JDEVELOPER:



You can use following API in process request :
public void processRequest(OAPageContext pageContext, OAWebBean webBean)
{
super.processRequest(pageContext, webBean);
//SEE ALL PARAMETER values you need to pass in the java comments of the method
addScrollBarsToTable(pageContext, webBean,"DivStart","DivEnd",true,"400",true,"400");
.
.
}


/**
* @param pageContext the current OA page context.
* @param webBean the web bean corresponding to the region.Pass the webBean of pagelayout.
* @param preRawTextBean is name of the Rawtext bean just above the table bean.
* @param postRawTextBean is name of the Rawtext bean just below the table bean.
* @param horizontal_scroll is boolean value if horizontal scrollbar is required/not required.
* @param width is the minimum width of table beyond which horizontal scrollbar is displayed.i.e
* if width is greater than this horizontal scrollbar is displayed.If this is passed as null, defalt value
* is considered which is 400.
* @param vertical_scroll is boolean value if vertical scrollbar is required/not required.
* @param height is the minimum height of table beyond which horizontal scrollbar is displayed .i.e
* if width is greater than this horizontal scrollbar is displayed.If this is passed as null, defalt value
* is considered which is 400.
*/
public void addScrollBarsToTable(OAPageContext pageContext,
OAWebBean webBean,
String preRawTextBean,
String postRawTextBean,
boolean horizontal_scroll, String width,
boolean vertical_scroll, String height)
{
String l_height = "400";
String l_width = "400";
pageContext.putMetaTag("toHeight",
"<style type=\"text/css\">.toHeight {height:24px; color:black;}</style>");

OARawTextBean startDIVTagRawBean =
(OARawTextBean) webBean.findChildRecursive(preRawTextBean);
if (startDIVTagRawBean == null)
{
throw new OAException("Not able to retrieve raw text bean just above the table bean. Please verify the id of pre raw text bean.");
}

OARawTextBean endDIVTagRawBean =
(OARawTextBean) webBean.findChildRecursive(postRawTextBean);
if (endDIVTagRawBean == null)
{
throw new OAException("Not able to retrieve raw text bean just below the table bean. Please verify the id of post raw text bean.");
}

if (!((height == null) || ("".equals(height))))
{
try
{
Integer.parseInt(height);
l_height = height;
}
catch (Exception e)
{
throw new OAException("Height should be an integer value.");
}
}


if (!((width == null) || ("".equals(width))))
{
try
{
Integer.parseInt(width);
l_width = width;
}
catch (Exception e)
{
throw new OAException("Width should be an integer value.");
}
}

String divtext = "";
if ((horizontal_scroll) && (vertical_scroll))
{
divtext =
"<DIV style='width:" + l_width + ";height:" + l_height + ";overflow:auto;padding-bottom:20px;border:0'>";
}
else if (horizontal_scroll)
{
divtext =
"<DIV style='width:" + l_width + ";overflow-x:auto;padding-bottom:20px;border:0'>";
}
else if (vertical_scroll)
{
divtext =
"<DIV style='height:" + l_height + ";overflow-y:auto;padding-bottom:20px;border:0'>";
}
else
{
throw new OAException("Both vertical and horizintal scrollbars are passed as false,hence, no scrollbars will be rendered.");
}
startDIVTagRawBean.setText(divtext);
endDIVTagRawBean.setText("</DIV>");
}
Please Note : Height and Width passed in addScrollBarsToTable API are the minimum
height and width of the table respectively which will be displayed on the page. Hence if this display resolution is available on page the scrollbars will not be displayed or corresponding one scrollbar or both scrollbars will be displayed.If you are not able see both scrollbars, even if you pass true for both in this method, try to put less resolution like 150 and 150 etc.

Happy coding..!

Friday, December 18, 2009

Blocking User on submit Action in a OAF page

This is one of the most frequently required feature in OAF page, in certain conditions. Basically our requirement is to block user from pressing a button twice, i.e.,if a button is pressed for the first time,till the time request processing is going on for the first request on server either the button should be disabled on page or an hourglass should be shown on the page, so that user should not be able to press it again to start another request, before this request's response has been received from server. This is very essential feature where

1) You are lets say creating a account.
2) Doing a transaction.
3) Generating a report.
4) Doing a transaction which takes significant amount of time which can make user impatient and press the button again.

The standard solution for this provided by OAF is setBlockOnEverySubmit API.Whenever a submit action takes place on a page, subsequent submits can be blocked.
When using a blocking on submit technique, when the submit action takes place, the cursor becomes busy and prevents any other submit action until the current submit event has been handled.
The block on submit behavior is not enabled by default on a page. However, For Partial Page Refresh (PPR) events alone, it is enabled by default.
To implement the block on submit behavior on a specfic page, add the following code to the processRequest() of that page CO:

import oracle.apps.fnd.framework.webui.beans.OABodyBean;

public void processRequest(OAPageContext pageContext, OAWebBean webBean)
{
super.processRequest(pageContext, webBean);
OAWebBean body = pageContext.getRootWebBean();
if (body instanceof OABodyBean)
{
((OABodyBean)body).setBlockOnEverySubmit(true);
}
...
...
}

But there are exceptions to this API functionality, what if my page has multiple buttons and I want this nature only on press of one of the button.Situations like this the standard solution is using OAProcessing page(See dev guide for OAProcessing page implementation, its explained well!), which is typically used in long running processed.But what if user is not ready to leave the page at all and asks you to block the navigation somehow? What if you are mobile browser, where OAProcessing page does not work fine. Situations like this can be taken care through javascript.
I must repeat again, use this javascript solution only when your requirement is not fulfilled by standard setBlockOnEverySubmit API provided by framework.Ok here are the steps for the javascript solution, which will block the action by making the button disabled as soon as it is pressed and releasing it as soon as your action is finished :

1) Add a button bean , instead of submit button bean. Lets say its id is "item1" in property inspector. Set destination uri property in property inspector as :
javascript:document.getElementById('item1').disabled=true;submitForm('DefaultFormName',0,{'XXX':'Y'});

2) In process form request, you can catch the event as :

public void processFormRequest(OAPageContext pageContext,
OAWebBean webBean)
{
super.processFormRequest(pageContext, webBean);

.// the js button click event
// don't use pageContext.getParameter() API to get parameter values
// in R12 because its doing validation on JS paremeters.
// so,instead of getting parameter value from pagecontext
//getting directly from http request.
if(pageContext.getRenderingContext()
.getServletRequest().getParameter("XXX")!=null)
{
// ur button event logic in process form request
......

//remove this parameter from http request
pageContext.removeParameter("XXX");
}
}

I hope this helps to people which are facing such situations. Happy coding..!

Tuesday, December 15, 2009

Migration of AOL setups from one instance to Another.

Hi All,
Typically this is the requirement of every Apps project whether its a upgrade/implementation/support. You might create AOL objects like profiles,alerts,AK regions etc. , here I am listing all the LDT commands which will help you in migration.LDT download command extract the AOL object in a .ldt file and then u can put that in target instance and call upload LDT command to upload it in target instance.
I think there are numerous websites where this is listed, but still commands like ldt of alert, ak region is still hard to find. Hence, I am consolidating all of them at one place :

function scripts
------------------
$FND_TOP/bin/FNDLOAD apps/apps 0 Y DOWNLOAD $FND_TOP/patch/115/import/afsload.lct XXADAT_MAT_REQ_HIST.ldt FUNCTION FUNCTION_NAME=XXADAT_MAT_REQ_HIST

$FND_TOP/bin/FNDLOAD apps/apps 0 Y UPLOAD $FND_TOP/patch/115/import/afsload.lct XXADAT_MAT_REQ_HIST.ldt


responsibility scripts
------------------------
FNDLOAD apps/apps O Y DOWNLOAD $FND_TOP/patch/115/import/afscursp.lct XXADAT_PROD_TIME_BOOKING.ldt FND_RESPONSIBILITY RESP_KEY="XXADAT_PROD_TIME_BOOKING"

$FNDLOAD apps/apps O Y UPLOAD $FND_TOP/patch/115/import/afscursp.lct file_name.ldt


menu scripts
-------------
FNDLOAD apps/apps O Y DOWNLOAD $FND_TOP/patch/115/import/afsload.lct AHL_PRD_MBENCH_USER_SUB_MENU.ldt MENU MENU_NAME="AHL_PRD_MBENCH_USER_SUB_MENU"

$FNDLOAD apps/apps O Y UPLOAD $FND_TOP/patch/115/import/afsload.lct file_name.ldt

lookup scripts
--------------
FNDLOAD apps/apps 0 Y DOWNLOAD $FND_TOP/patch/115/import/aflvmlu.lct AHL_PRD_DEFERRAL_TYPE.ldt FND_LOOKUP_TYPE APPLICATION_SHORT_NAME ="XXADAT" LOOKUP_TYPE="AHL_PRD_DEFERRAL_TYPE"

FNDLOAD apps/apps 0 Y UPLOAD $FND_TOP/patch/115/import/aflvmlu.lct XXADAT_NO_CONCURRENT_LOGIN_WOS.ldt

ak region download script
-------------------------------
java oracle.apps.ak.akload apps apps THIN "(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(Host = < instance IP > )(Port = 1541)) (CONNECT_DATA = (SID=DEV)))" DOWNLOAD XXADAT_EM_LOV.ldt GET CUSTOM_REGION AHL XXADAT_EM_LOV

ak region upload script
------------------------
java oracle.apps.ak.akload apps apps THIN "(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(Host = < instance IP > )(Port = 1620)) (CONNECT_DATA = (SID=RTST)))" UPLOAD XXADAT_EM_LOV.ldt UPDATE CUSTOM_REGION

profile scripts
---------------
FNDLOAD apps/apps O Y DOWNLOAD $FND_TOP/patch/115/import/afscprof.lct XXADAT_BUDGET_MANHOURS_NROUTINE.ldt PROFILE PROFILE_NAME="XXADAT_BUDGET_MANHOURS_NROUTINE" APPLICATION_SHORT_NAME="XXADAT"

FNDLOAD apps/apps O Y UPLOAD $FND_TOP/patch/115/import/afscprof.lct XXADAT_BUDGET_MANHOURS_ROUTINE.ldt

message scripts
----------------
FNDLOAD apps/apps 0 Y DOWNLOAD $FND_TOP/patch/115/import/afmdmsg.lct XXADAT_MIN_EXP_MSG.ldt FND_NEW_MESSAGES APPLICATION_SHORT_NAME="XXADAT" MESSAGE_NAME="XXADAT_MIN_EXP_MSG"

FNDLOAD apps/apps 0 Y UPLOAD $FND_TOP/patch/115/import/afmdmsg.lct XXADAT_MIN_EXP_MSG.ldt

CONCURRENT PROGRAM
--------------------
FNDLOAD apps/appsrtst O Y DOWNLOAD $FND_TOP/patch/115/import/afcpprog.lct XXADAT_CL_COMP_WO_F_VISIT.ldt PROGRAM APPLICATION_SHORT_NAME="XXADAT" CONCURRENT_PROGRAM_NAME="XXADAT_CL_COMP_WO_F_VISIT"

FNDLOAD apps/apps O Y UPLOAD $FND_TOP/patch/115/import/afcpprog.lct XXADAT_CL_COMP_WO_F_VISIT.ldt

alert
------------
FNDLOAD apps/apps 0 Y DOWNLOAD $ALR_TOP/patch/115/import/alr.lct XXADAT_INST_LOC_UPDATE.ldt ALR_ALERTS APPLICATION_SHORT_NAME='XXADAT' ALERT_NAME= 'XXADAT_INST_LOC_UPDATE'

PS:This ldt command brings all alerts in the instance in the ldt file, so u need to remove all except the one u want to move.


FNDLOAD apps/apps 0 Y UPLOAD $ALR_TOP/patch/115/import/alr.lct XXADAT_RESET_NR_TRACKING_NO_WHN_DEF.ldt

Workflow Upload
-----------------
WFLOAD apps/apps 0 Y UPLOAD XXCLOSNR.wft

In all these commands apps/apps is username/password of apps database user. Also, in all upload commands you can give full location from where your ldt/wft file should be picked to upload. I hope this helps.

Monday, December 7, 2009

Programatic PPR in OAF.

I was recently helping a friend in a customization in OAF, where through personalization, he wanted to put a ppr action in a seeded page in a seeded item. He was trying to do that customization using fire action, which was not acceptable by customer due to obvious reasons. Since, I think this section is missing in developers' guide, and is quite simple to approach, here is code you can write in process request of CO to attach programmatic PPR. lets take a simple example of attaching PPR to a message choice bean :

//In Process Request()
{
//Please attach PPR only to those UIX beans which support it
//otherwise it may not work
OAMessageChoiceBean mcb=(OAMessageChoiceBean)webBean.findChildRecursive("");
FireAction firePartialAction = new FirePartialAction("empPositionChange");
mcb.setAttributeValue(PRIMARY_CLIENT_ACTION_ATTR,firePartialAction);
}

//In process form request
if ("empPositionChange".equals(pageContext.getParameter(OAWebBeanConstants.EVENT_PARAM)))
{
//ur logic on PPR

//if PPR is attached in a table/hgrid child then we can find the row
//reference by
String rowReference =pageContext.getParameter(OAWebBeanConstants.EVENT_SOURCE_ROW_REFERENCE);
}

I hope this helps. Happy oaf extensions :)!