Thursday, October 2, 2014

OAF - Personalize Differently!

Was working on Oracle EBS OAF personalization, and wow it is one hack of a framework. For a long time, I thought APEX and ADF had to the bottom of intelligence pyramid, but OAF broke that by million points!

There are tons of restriction on what you can do and not to do with OAF personalization framework, and yeah you can get access to controller and do a lot more as you get access to pageContext and webBean, but there are still tons of limitation. E.g. adding table column with multiple icons in it (not advance table) or adding switcher column on existing table with icons, etc.. Here is another approach which might work better. Integrate it with JQuery and then use JQuery's power to do anything you like on the page. As we all know, with JQuery - sky is the limit!

Here is how we did it :

Step 1 : Upload JQuery to EBS

  • Copy JQuery related files to EBS server ($OA_MEDIA/xx_demo/images):
    • Copy all images which comes with JQuery to
  • Copy all JS files to EBS server ($OA_HTML/xx_demo/js):
    • jquery-1.11.0.min.js
    • jquery-ui-1.10.3.custom.min.js
  • Copy all CSS files to EBS server $OA_HTML/xx_demo/css
    • jquery.ui.all.css (alternatively you can all copy jquery-ui.min.css)
  •  Update CSS files for the image reference. 
    • Search/Replace images/ -> /OA_MEDIA/xx_demo/images/
  •  Create a custom JS file
    • This file will contain all your customization needs to be done.
    • I created xx_demo_oaf_customization.js
    • Copied this file to $OA_HTML/xx_demo/js
    • This file will have function called xx_oaf_customize which will be called when page is fully loaded

Step 2 : Load JQuery on EBS page

This is sensitive piece. There are multiple way to do so, and each one has it's plus/minus:

Option 1 : Modify OAPageBean class
  • Decompile $JAVA_TOP/oracle/apps/fnd/framework/webui/OAPageBean.class
  • Under addJavascriptForNewFeatures function, you can add JQuery libraries as below:
 mPageContext.putJavaScriptLibrary("jqCore", "/OA_HTML/xx_demo/js/jquery-1.11.0.min.js");            
 mPageContext.putJavaScriptLibrary("jqCore", "/OA_HTML/xx_demo/js/jquery-ui-1.10.3.custom.min.js");            
 mPageContext.putJavaScriptLibrary("jqCore", "/OA_HTML/xx_demo/js/xx_demo_oaf_customization.js");            
  • Recompile and put it back, in this case the JQuery will be loaded on all OAF page in system
  • This is probably not very good idea

Option 2 : Customize/Extend the controller

  • Write a controller and extend it. Controller will provide access to webBean and pageContext
  • From page context, we can add JS as below 
 mPageContext.putJavaScriptLibrary("jqCore", "/OA_HTML/xx_demo/js/jquery-1.11.0.min.js");            
 mPageContext.putJavaScriptLibrary("jqCore", "/OA_HTML/xx_demo/js/jquery-ui-1.10.3.custom.min.js");            
 mPageContext.putJavaScriptLibrary("jqCore", "/OA_HTML/xx_demo/js/xx_demo_oaf_customization.js");            
  •  We can attach this controller to a specific page and then JQuery will be loaded on that page

Option 3 : Customize oafcoreR121.js
  • Any OAF page includes $OA_HTML/cabo/oajsLibs/oafcoreR121.js, so I added a few lines of code in oafcoreR121.js to load 
    • JQuery related javascript
    • CSS files and 
    • then call xx_oaf_customize function (in xx_demo_oaf_customization.js file) when page is completely done with loading. 
  • Code I put inside oafcoreR121.js is as below or you can download here:
      /* XX OAF Customization to load JQuery : START */  
      /* generic function load script */  
      /* note: we could certainly use below, but jquery is not yet loaded. */  
      /* loadScript(jsURL, callback); */  
      function xx_loadScript(url, callback) {  
           var head = document.getElementsByTagName('head')[0];  
           var script = document.createElement('script');  
           script.type = 'text/javascript';  
           script.src = url;  
           script.onreadystatechange = callback;  
           script.onload = callback;  
      /* XX load jquery core js */  
      xx_loadScript("/OA_HTML/xx_demo/js/jquery-1.11.0.min.js", xx_jquery_js_loaded);  
      /* XX load jquery UI js */  
      function xx_jquery_js_loaded() {  
           xx_loadScript("/OA_HTML/xx_demo/js/jquery-ui-1.10.3.custom.min.js", xx_jquery_ui_js_loaded);  
      /* XX load oaf customization js */  
      function xx_jquery_ui_js_loaded() {  
           xx_loadScript("/OA_HTML/xx_demo/js/xx_demo_oaf_customization.js", xx_jquery_xx_demo_oaf_customization_js_loaded);  
      /* XX load css, and call entry point */  
      function xx_jquery_xx_demo_oaf_customization_js_loaded() {  
           $("<link/>", { rel: "stylesheet", type: "text/css", href: "/OA_HTML/xx_demo/css/jquery.ui.all.css"}).appendTo("head");  
           $(document).ready(function() {  
      /* XX OAF Customization to load JQuery : END */  

Step 3 : Personalization Begins!
Now you can do anything you would normally do as JQuery guru. For small example, I had a table below in EBS, and I just wanted to add column with multiple rows.

This is how I did inside xx_demo_oaf_customization.js file: - download here.

 function xx_oaf_customize() {  
      if( $("[id$=DefaultFormName]") && $("[id$=DefaultFormName]").attr('action') ) {  
           if( $("[id$=DefaultFormName]").attr('action').indexOf("IcxPorRcvHomePG") > -1 ) {  
                // iProcurment.Receiving Page Found  
                console.log(' Receipt Page Found ' );  
                if( $("[id$=ReceiptsTableRN]") ) {  
                     // Table Region Found  
                     console.log(' ReceiptsTableRN region found ' );  
                     if( $("[id$=ReceiptsTableRN]").find("table.x1h") ) {  
                          console.log(' Table found ' );  
                          $("[id$=ReceiptsTableRN]").find("table.x1h").find("th:last").after("<th class='x1t x4m' scope='col'> <span id='XX_OASH__643' class='x24'>Demo Actions</span></th>");  
                          $("[id$=ReceiptsTableRN]").find("table.x1h").find("tr").each( function() {  
                               if( $(this).find('td') ) {  
                                    $(this).find('td:last').after("<td class='x1p x50'> <a id='XX1:ViewDetailImage:0' href='' onclick='' name='XX1:ViewDetailImage:0'> <img border='0' align='middle' title='View Details' src='/OA_MEDIA/eyeglasses_24x24_transparent.gif'> </a> <a id='XX2:ViewDetailImage:0' href='' onclick='' name='XX2:ViewDetailImage:0'> <img border='0' align='middle' title='View Details' src='/OA_MEDIA/xx_demo/images/details_close.png'> </a> <a id='XX3:ViewDetailImage:0' href='' onclick='' name='XX3:ViewDetailImage:0'> <img border='0' align='middle' title='View Details' src='/OA_MEDIA/xx_demo/images/details_open.png'> </a></td>");  

Now OAF page has the column I need: