Sunday, November 29, 2015

PeopleTools 8.54+ - Branding - Part 5C - Fluid Branding (Continued)

This is a continuation of my previous posts on PeopleTools Fluid Branding: Part 5A and Part 5B.

Using jQuery to override Fluid Branding Images and Icons:

Those who read my previous post (Part 5A) would realize that in order to override the delivered images and icons on the Fluid Branding we ended up customizing some of the Fluid pages and subpages. We did that because all those images and icons were referenced directly on the page field properties of the Fluid header/footer.

The question is whether we can use CSS, javascript or a combination of both to override these images and icons? The answer is yes but I would continue to ask the question if the juice is worth the squeeze? Do we really need all this additional client side code/overrides just to avoid a handful of customizations to page field properties? I will let you make that determination depending on your circumstances and environment.

In this section I will try to provide an alternative option using jQuery to avoid making customizations for image/icon overrides. You could also achieve the same using pure CSS, javascript or both but I find the jQuery library quite useful for such DOM manipulation.

As an example for the purpose of this section, I reverted the change I made previously to override the Fluid home logo image. Now the page field property is as delivered and we can see the Oracle logo on the Fluid homepage when we login.

App Designer > File > Open > Page > Select: PTNUI_LP_HEADER



In my previous post (Part 5B), I detailed how I created a online configuration framework to inject javascripts into Fluid UI. I used this framework to inject jQuery (PT_JQUERY_1_6_2_JS) and my custom javascript object (CSK_FL_IR_JS) for image replacement.


Now let us take a look at the javascript object CSK_FL_IR_JS.


In this javascript, I called an IScript that would resolve the suitable URL on the web server (cache directory) for the custom image object CSK_LOGO_SVG and store it in a javascript variable called 'cskLogo' (refer Part 5B for more details). Then I used jQuery to set/override the 'src' attribute for the image element with id 'PTNUI_LP_HEADER_PTNUI_LOGO'.

JavaScript for reference:

// Get Image URL for CSK_LOGO;
document.write(unescape("%3Cscript src='/psc/" + site + "/EMPLOYEE/EMPL/s/WEBLIB_FL_CSK.ISCRIPT1.FieldFormula.IScript_Set_Image_URL_Variable?var=cskLogo&img=CSK_LOGO_SVG' type='text/javascript'%3E%3C/script%3E"));

var $jq = jQuery.noConflict();
$jq(document).ready(function(){

  $jq('#PTNUI_LP_HEADER_PTNUI_LOGO img').attr('src', cskLogo);

});


Now we can test the changes and see how we could simply override the source attribute of an image using jQuery to avoid customizations to page field properties.


Creating an environment specific header for Non-Production databases:

This section is intended to help us avoid what I like to call "testing in production"! I am sure we have all worked in IT long enough to realize that accidents happen. I have seen many cases where we end up making changes in the production environment without realizing we are in production (causing all sorts of issues). This typically happens when we have end users and/or testers working on multiple environments across multiple browser windows. Traditionally, most organizations (with PeopleSoft applications) tend to add some visual cues (such as writing the database name on the header) to indicate/differentiate the fact that the user is currently logged in to a test environment.

In Classic UI, we had plenty of real estate in the branding header to add the database name. In Fluid UI, since we might not have enough wiggle room in the delivered header we will need to be a bit more creative. My approach to this requirement for Fluid is to add an additional custom header (that can be hidden - in cases were we want to take screenshots for job aids, etc.). While it does take up a small portion of the real estate, this was the least intrusive method that I could think of (without affecting existing functionality around the Fluid branding header).

Step 1: Create custom javascript object


This custom javascript object uses the jQuery library to add (using 'prepend' method) a custom div to the beginning of the delivered header element with id='PT_HEADER'.


Custom div structure:
div (id = cskdbnamecontainer)
- Text with database name (id = cskdbname)
- 'Hide' link to remove the header if required (id = cskdbnamehide)

Additionally, the javascript object (CSK_FL_DBNAME) also contains code to remove the custom div (cskdbnamecontainer) when we click on the 'Hide' link.

Javascript for reference:

// Display Database Name using jQuery once the document is ready;

var $jq1 = jQuery.noConflict();

$jq1(document).ready(function(){

   // Add div to display DB Name;
   $jq1( "#PT_HEADER" ).prepend( "<div id='cskdbnamecontainer' align='center'><span id='cskdbname'>Test Environment: %dbname</span><a href='#' id='cskdbnamehide'>Hide</a></div>" );

   // Add jQuery to hide the DB Name when the 'Hide' link is clicked;
   $jq1("#cskdbnamehide").click(function(){
         $jq1("#cskdbnamecontainer").hide();
   });

});


Step 2: Inject custom styles

In part 5A, we saw how we could inject custom styles into Fluid UI. Now let us add some additional custom styles to CSK_FLUID_CSS style sheet object so that it gets injected at runtime.

Note: If you are performing these steps in PT 8.55, then instead of using CSK_FLUID_CSS, you will need to make the changes to CSK_BRAND_FLUID_TEMPLATE (Click here for more details).


Custom styles for reference:

/* Fluid - Display DBName for non-Prod environments */
#cskdbnamecontainer {
   height: 26px;  
   background-color: rgb(204,204,204);
}
#cskdbname {
   color: rgba(229,47,0,1);
   line-height: 26px;
   font-weight: bold;
   padding-right: 10px;
}


Step 3: Inject custom javascript CSK_FL_DBNAME into Fluid UI

Let us inject this javascript for all non production databases (we don't want to display this in production).


Javascript injection snippet for reference:

// CSK Add Custom Environment Header;
if ("%dbname" !== "PROD_DBNAME") {

  document.write(unescape("%3Cscript src='/psc/" + site + "/EMPLOYEE/EMPL/s/WEBLIB_PTBR.ISCRIPT1.FieldFormula.IScript_GET_JS?ID=CSK_FL_DBNAME' type='text/javascript'%3E%3C/script%3E"));

}


Now we are ready to test this change.




If we click on the 'Hide' link on the custom header, then the entire div gets removed as shown below.



Adding Custom Links to Fluid Branding:

In my previous posts (Part 3 and Part 4B), we saw how we could add custom links to the Classic Branding Header. In this section, I will detail how we can add custom links to the Fluid Branding. Fluid Header as such has very limited real estate (unlike Classic), so we need to find the appropriate location to add the custom links without adversely affecting any of the delivered functionality. The best way to add custom links (with minimal impact) would be as additional list items on the "Action Menu" aka the "Hamburger" icon.



Let us say, we want to add a custom link to an external website which should be available on all Fluid Pages (including the Landing Page).

How do we add a custom link to the Fluid "Action Menu"? First, we need to figure out how the Action Menu is getting generated. If we open PT_HEADER_ACTION page in Application Designer then we can see that all delivered links are list on this page. We can also see that each delivered link is wrapped in a group box and contains specific styling. The links on the PT_HEADER_ACTION page are additionally controlled via JavaScript, CSS media queries and/or PeopleCode to display/hide links according to the device form factor. In our case, we want our custom link to always appear on all pages regardless of device form factor.


Notice there is a small area right at the top of the page which contains a "Custom Action" group box? It appears that Oracle has already anticipated the need for custom Action Menu links and provided a placeholder group box. Let us see how we can add our custom link(s) into this group box.

Step 1: Create Custom Subpage

Instead of directly adding our custom links to the PT_HEADER_ACTION page, let us create a custom subpage so that it acts as a container for all the custom links on the Fluid Branding.

The custom subpage (CSK_HEADER_ACTION) contains just two fields (one groupbox for styling and one hyperlink which is the custom link).



Page Field Properties for Group Box "CSK Home":

Add 'Default Style Name' as ps_menuitem.


Page Field Properties for Hyperlink "CSK Home":

Set the Hyperlink Destination to 'PeopleCode Command' so we can control the OnClick event using the FieldChange PeopleCode.


Always better to use a Message Catalog entry for Labels.


Step 2: Write Custom PeopleCode for the Custom Link FieldChange Event


PeopleCode for Reference:

Local string &redirectURL;

&redirectURL = GetURL(URL.CSK_FLU_HOME_ACTION_ITEM);

%Response.RedirectURL(&redirectURL);


URL Definition for Reference:


Updated on 04/26/2018

Please refer the below blog post for updated code/logic.
https://pe0ples0ft.blogspot.com/2018/04/adding-custom-links-to-action-menu.html

Step 3: Add Custom Subpage to PT_HEADER_ACTION

This section updated on August 26, 2017:

Gaurav Rajput reported an issue with Custom Action List items which made me realize that adding our custom subpage inside the 'Custom Action' groupbox was interfering with other custom (page level) action items that are included at a transaction level (click here for more details). To fix the reported issue, I moved the custom subpage outside the 'Custom Action' groupbox as follows (regardless of PeopleTools version this is a better approach moving forward):


Order of the custom subpage should also be above the 'Custom Actions' groupbox:


We are now ready to test these changes.




Note: The above steps detail how we can add custom links to Fluid universally across the application (for all pages). If you want to add custom links to a specific page (custom or delivered) only, then you can follow the approach used on PT_LANDINGPAGE (delivered page) to add the 'Personalize' link.


Considering Small Form Factor Logo for Fluid Branding:

If you used the approach detailed in Part 5A for overriding the Fluid Homepage Logo then you might have noticed that the logo for small form factor (SFF) devices revert back to another delivered image (PTNUI_ORACLE_LOGO_SFF).


This delivered image is generated by some runtime PageActivate PeopleCode.


We have two options to override this small form factor image.

Option 1: This applies for anyone currently using the customization approach detailed in Part 5A. We can simply customize PT_LANDINGPAGE.Activate (Page PeopleCode) to use our custom SFF image instead of the delivered as follows:


Option 2: Alternatively, if we use the jQuery image replacement technique detailed above then we can improvise the javascript to appropriately replace the logo depending on the form factor.

 Update CSK_FL_IR_JS as follows:


Updated javascript for reference:

// Get Image URL for CSK_LOGO;
document.write(unescape("%3Cscript src='/psc/" + site + "/EMPLOYEE/EMPL/s/WEBLIB_FL_CSK.ISCRIPT1.FieldFormula.IScript_Set_Image_URL_Variable?var=cskLogo&img=CSK_LOGO_SVG' type='text/javascript'%3E%3C/script%3E"));

// Get Image URL for CSK_LOGO (SFF);
document.write(unescape("%3Cscript src='/psc/" + site + "/EMPLOYEE/EMPL/s/WEBLIB_FL_CSK.ISCRIPT1.FieldFormula.IScript_Set_Image_URL_Variable?var=cskLogoSFF&img=CSK_LOGO_SFF_SVG' type='text/javascript'%3E%3C/script%3E"));

var $jq = jQuery.noConflict();
$jq(document).ready(function(){

  // Get current Image Source;
  var imageSrc = $jq('#PTNUI_LP_HEADER_PTNUI_LOGO img').attr('src');


  if (imageSrc != null) {
    if (imageSrc.indexOf("PTNUI_ORACLE_LOGO_SFF") < 0) {

      // Use Regular Image;
      $jq('#PTNUI_LP_HEADER_PTNUI_LOGO img').attr('src', cskLogo);

    } else {

      // Use Small Form Factor Image;
      $jq('#PTNUI_LP_HEADER_PTNUI_LOGO img').attr('src', cskLogoSFF);

    }
  }



});


Result:


Overriding the Favorite Icon for Fluid:

I am not sure if many of us noticed this but there is a delivered icon image (LOGO_FAVICON) on every browser tab beside the page title for all Fluid pages. This image/icon is called the Favorite Icon.



Someone has already asked Oracle on how to change this favorite icon image? According to Doc ID 2004996.1, currently there is no way to override this image other than updating the delivered image (LOGO_FAVICON) with a custom image using App Designer. Also, there might be plans to incorporate this as a configurable item in the future releases. You may choose to perform the steps detailed in the Oracle Support document.

Refer the following document on My Oracle Support for more details:
E:FLUID- How To Access Favicon On Fluid Landing Pages? (Doc ID 2004996.1)

I don't think I like the idea of replacing the delivered image LOGO_FAVICON (as suggested in Doc ID 2004996.1). There would be no way to identify that we made a customization since it is an image object.

Let us look at the option of using the jQuery image replacement technique detailed in the preceding section for overriding this delivered image LOGO_FAVICON.

Once again, I am updating my image replacement javascript object CSK_FL_IR_JS. I am finding all the occurrences of LOGO_FAVICON (delivered image) under head -> link (where href attribute contains LOGO_FAVICON) and replacing it with a custom image (CSK_LOGO_FAVICON) using jQuery.


Updated javascript (CSK_FL_IR_JS) for reference:

// Get Image URL for CSK_LOGO (FavIcon);
document.write(unescape("%3Cscript src='/psc/" + site + "/EMPLOYEE/EMPL/s/WEBLIB_FL_CSK.ISCRIPT1.FieldFormula.IScript_Set_Image_URL_Variable?var=cskFavIcon&img=CSK_LOGO_FAVICON' type='text/javascript'%3E%3C/script%3E"));

// Get Image URL for CSK_LOGO (Fluid);
document.write(unescape("%3Cscript src='/psc/" + site + "/EMPLOYEE/EMPL/s/WEBLIB_FL_CSK.ISCRIPT1.FieldFormula.IScript_Set_Image_URL_Variable?var=cskLogo&img=CSK_LOGO_SVG' type='text/javascript'%3E%3C/script%3E"));

// Get Image URL for CSK_LOGO (Fluid SFF);
document.write(unescape("%3Cscript src='/psc/" + site + "/EMPLOYEE/EMPL/s/WEBLIB_FL_CSK.ISCRIPT1.FieldFormula.IScript_Set_Image_URL_Variable?var=cskLogoSFF&img=CSK_LOGO_SFF_SVG' type='text/javascript'%3E%3C/script%3E"));

var $jq = jQuery.noConflict();
$jq(document).ready(function(){

  // Get current Image Source;
  var imageSrc = $jq('#PTNUI_LP_HEADER_PTNUI_LOGO img').attr('src');

  if (imageSrc != null) {
    if (imageSrc.indexOf("PTNUI_ORACLE_LOGO_SFF") < 0) {

      // Use Regular Image;
      $jq('#PTNUI_LP_HEADER_PTNUI_LOGO img').attr('src', cskLogo);

    } else {

      // Use Small Form Factor Image;
      $jq('#PTNUI_LP_HEADER_PTNUI_LOGO img').attr('src', cskLogoSFF);

    }
  }

  // Replace Browser Favorite Icon(s);
  $jq('head link[href*=LOGO_FAVICON]').attr('href', cskFavIcon);

});


We are ready to test these changes now. We can see from the screenshot below that all occurrences of LOGO_FAVICON (delivered image) have been replaced with CSK_LOGO_FAVICON (custom image).

19 comments:

  1. Hi Sasank,

    Good material you have got here, excellent writing!

    I noticed that when you add a group box on your fluid page of the type "Custom Action Menu" and drop your subpage CSK_HEADER_ACTION inside, the 'hamburger'-menu will automatically have your new menu item. This way you can extend the menu without touching the original PT_HEADER_ACTION. Just wanted to put my two cents in...

    If you want to do the same for your landing page, you probably need to clone it first. (or create one from the landing page template).

    Keep up the good stuff!

    Stefan van Liempt

    ReplyDelete
    Replies
    1. Hey Stefan,

      Good point. I was going to write and expand on that topic but I missed it. You are right, that is a great way to add custom links to our Fluid pages without having to customize other pages. Thanks for mentioning that!

      In the particular case I described, I was looking at a way to add custom links across the board (entire system). Similar to how we used to add links to frequently visited pages on the Branding Header in Classic. So that is why I went with this approach which provides a way to universally add links to Fluid.

      Thanks for the comment! I am sure others visiting this page would benefit from that idea.

      Sasank

      Delete
  2. Hi Sasank,

    Thank you for all the awesome branding posts for 8.54! I have used them extensively since we upgraded to 8.54 a few weeks ago.

    How would you recommend adding a Favicon with classic (not fluid)? We had previously implemented a solution similar to the link below, but this does not seem to work since the upgrade.
    http://peoplesofttipster.com/2011/05/12/changing-the-favicon-in-peoplesoft-the-how-to/

    Thanks!
    Derek H.

    ReplyDelete
    Replies
    1. Hi Derek,

      I did not think about that one. I see that PeopleSoft have stopped generating a favicon similar to the way that Duncan Davies has suggested in the link you posted. I wonder if it had to do with the changing browser support for favicon? I am not really sure.

      You could use the same jQuery approach that I used here for Fluid because it appears that the jQuery library is available in Classic as well. Again, I am not sure if it would be advisable considering browser support, etc.

      But I tried to create a little javascript do add the link element to the head and it seems to work for the most part. You may have to tweak the code a bit more if you want to dynamically use an object from the database versus picking something from the web server.

      Here is a link to a few screenshots detailing what I did:
      http://pe0ples0ft.blogspot.com/p/branding-peopletools-854-favicon-for.html

      Let me know your thoughts and how you end up implementing the favicon for Classic!
      Thanks,
      Sasank

      Delete
    2. Thanks for this write-up Sasank! My solution is very similar to yours, but I went the dynamic route instead of the web server. I created a basic HTML element on Define Headers and Footers (faviconHolder), put the logo in there with JS then hid it. I am a new developer and didn’t think about creating the div in JavaScript directly, but I will try that soon.

      Then I use the CSS and JavaScript below:
      CSS
      .favicon {}
      .favicon:before {
      content: url(%Image(MY_FAVICON));
      }

      JavaScript
      // Set favicon class;
      document.getElementById("faviconHolder").classList.add("favicon");

      // A hidden div gets the favicon URL. This hides the div.
      document.getElementById('faviconHolder').style.display = "none";

      // Get URL of the favicon;
      var favicon = window.getComputedStyle(document.querySelector('.favicon'), ':before').getPropertyValue('content');
      var favicon = favicon.slice(5,-2);

      // Create link to favicon;
      var link = document.createElement('link');
      link.type = 'image/x-icon';
      link.rel = 'shortcut icon';
      link.href = favicon;

      // Add link to head;
      document.getElementsByTagName('head')[0].appendChild(link);

      Delete
    3. @Derek - Thanks for sharing. Very creative. I think your approach is better than mine.

      I was also trying to put my JS on the header definition but could not think of a clean way to do it. I like your idea! I would still with it rather than using my suggestion.

      Delete
  3. Hi Sasank,

    This is really helpful.

    We are trying to hide the Personalize link under the action menu using the peoplecode, but the group box is not getting removed. do you have any suggestion to remove the Personalize link, we dont want to our users to add customize items on home page.

    PTNUI_LP_HEADER.PTNUI_ACTION_LINK.Visible = False;
    PTNUI_LAND_WRK.GROUPBOX15.Visible = False;

    Thanks!

    ReplyDelete
    Replies
    1. The 'Personalize' link can be controlled using security. You can simply follow the instructions in the following My Oracle Support document:
      E-FLUID: How To Disable The "Personalize" Menu Option In Fluid Mode? (Doc ID 2118980.1)

      Delete
  4. Hi Sasank - this is fantastic! We have been trying to find a way to display the environment name in fluid for our non-Production environments. Do you know how we could also display the oprid in the environment specific header? I've tried %userid. This appeared to work, but seems to be pulling the owner of the javascript object (maybe?) It displays my oprid, even when logged in as another user. Browser cache was not the issue.

    Thanks again for all of your posts - incredibly helpful!
    Elizabeth

    ReplyDelete
    Replies
    1. Elizabeth - Excellent question. It had me stumped for a little bit. I tried to replace %dbname on my 8.55 environment (HCM 9.2 PUM 17) with %userid and I saw the same results. First, I thought it was probably due to homepage caching (a setting that can be configured at the web profile level) which is usually ON by default. That wasn't the case either.

      The reason why the %userid is not dynamic is because the javascript object is getting cached on the web server (web domain cache folder).

      WEBLIB_PTBR.ISCRIPT1.FieldFormula.IScript_GET_JS?ID=CSK_FL_DBNAME is going to use the delivered IScript (IScript_GET_JS) which in turn uses %Response.GetJavaScriptURL(@("HTML." | &Id));

      So end result is that the meta-HTML in the javscript gets resolved the first time and then is cached on the web server for subsequent requests. So using the %userid meta-html in this sort of framework (described in this post) might not work. You may have to create your own IScript that dynamically provides the userid per request instead of using the %userid meta-html.

      Thanks for sharing your experience. Hope this helps and makes sense!

      Delete
  5. Hi Sasank,

    Your document is excellent. I followed your steps to display the non-prod DB names, but I could not see the container getting built on my page and db name does not show up. Should I be clearing both app/web cache?

    Thanks!

    AD

    ReplyDelete
    Replies
    1. Yes. Try clearing your app/web server cache.

      If the issue persists, you might want to add some console.log messages in your javascript to log to the browser console. This is a very efficient way to debug and check if your javascript is getting executed. You will also find any related javascript errors/warnings on the browser console.

      Delete
  6. This comment has been removed by the author.

    ReplyDelete
  7. I HAD CREATED A FULID PAGE IN THAT I USE SAVE BUTTON USING TOOLBAR ACTION WHEN I PRESSING SAVE BUTTON,DATA GET SAVE BUT NOTHING POPUP (FOR EG:-SAVED SUCESSFULL) CAN HELP ME PLZZ
    I KNOW THAT I CAN USE PEOPLECODE BUT I WANT TO KNOW HOW TO DO USING ACTION TOOLBAR IT WILL BE GREAT IF YOU HELP ME

    AND HOW TO DEVELOP FLUID PAGE USING HTML,HOW I CAN USE JAVASCRIPT THANKS IN ADVANCE

    ReplyDelete
    Replies
    1. Hi Bipin - To my knowledge there is no delivered confirmation message that shows up in Fluid when you use the Toolbar Action - Save functionality.

      You can try using the following delivered PeopleCode Function:
      SetConfirmationMessage which is part of PT_WORK.PT_CONFIRM_MSG.FieldFormula.

      I used it in one of my experiments. Here is a link to the example code:
      Example Code

      Delete
  8. Thank you so much for this detail explanation. It is very helpful and informative. You mentioned above that you will need to make the changes to CSK_BRAND_FLUID_TEMPLATE (Click here for more details). - Not able to find code for this fluid template. Do you have code for this fluid template? Thank you so much.

    ReplyDelete
    Replies
    1. If you are on 8.55, you might want to start here and continue with the subsequent parts:
      https://pe0ples0ft.blogspot.com/2016/03/peopletools-855x-branding-part-i-what.html

      This has a list of all posts related to branding:
      https://pe0ples0ft.blogspot.com/2014/11/peopletools-854-branding-part-1.html#index

      CSK_BRAND_FLUID_TEMPLATE is a clone of the content in PT_BRAND_FLUID_TEMPLATE delivered style sheet.

      Delete
  9. Hi Sasank,

    We are trying to load QR code in HTMLAREA in Fluid Page which is generated using Javascript . It works in classic component .

    we tried the AddJavaScript and delivered AddOnLoadScript to call the javascript method . it throws error Custom java script method not found .the QR code is not loading on page active .

    if we click any object like grid or any button the QR code script loads in HTML Area.

    Can you guide us

    ReplyDelete
    Replies
    1. I am not sure if there is a timing issue with your javascript and when the various libraries load. Have you looked at this option for some ideas?

      https://pe0ples0ft.blogspot.com/2019/03/javascript-injection-framework-updates.html

      It uses requirejs to manage dependencies. You could use a similar approach.

      Delete