Wednesday, July 19, 2017

Event Mapping: Extending "Personal Details" in HCM

As you would expect, PeopleSoft's HCM self-service functionality allows employees to self-report many industry-generic, best-practice attributes. But none of us are industry-generic, which means we may have to capture more attributes than Oracle intended. The way I've seen many organizations handle this is to customize the delivered Personal Details pages to collect additional attributes. Although having an upgrade impact, customizing the classic versions of these pages makes a lot of sense. With continuous delivery, however, customers no longer apply massive upgrades, but rather iterative, incremental continuous upates. With this in mind, the cost of maintaining a customization is significantly higher than the traditional periodic upgrade model. A customization may be the only thing standing between you and important new features. Wouldn't it be nice to revert your personal details pages to vanilla?

Classic HCM housed several components that collectively represent "Personal Details." The Fluid iteration of Personal Details uses a design pattern akin to a WorkCenter to colocate the navigation for each of the Personal Details components. Rather than customize delivered components, what if we took any custom attributes and placed them in a separate component and then added that separate component to the list of Personal Details components?

The Personal Details tile of an HCM Employee Self Service landing page is a link to the employee address component (HR_EE_ADDR_FL). This component (or rather the primary page in the component) uses a two-panel layout to display a list of links on the left and a transaction area on the right. With a little bit of App Designer investigation, we see that the list on the left is really a Derived/Work disconnected Rowset populated through PeopleCode. Therefore, to add a link to the left-hand list, we need to insert rows into that disconnected Rowset. The question is, "How do we add a row to this list without modifying delivered PeopleCode?" The answer: Event Mapping. Related Content Event Mapping is an 8.55 PeopleTools feature that lets a developer map a PeopleCode event handler into a component event. What this means is we can write PeopleCode separate from Oracle's delivered PeopleCode and then map our PeopleCode into the same events already handled by Oracle. Since we are not intermingling our code with Oracle's, this is a configuration, not a customization.

Event Mapping configuration requires the following steps:

  1. Create an Application Class with mapped business logic,
  2. Define a Related Content Service Definition, and
  3. Map a component event to a Related Content Service Definition.

Before writing any PeopleCode, I recommend identifying your target event. Your PeopleCode has full access to the component buffer and executes in the same context as the target event handler. If your event handler targets RowInit of level 2, for example, PeopleCode functions such as GetRowset and GetRow will return the level 2 rowset or row respectively. Another reason to identify your target event first is because it is a good idea to have an understanding of the event PeopleCode you will be supplementing.

Oracle populates the left-hand list using component PostBuild PeopleCode. PostBuild is a great place to populate a navigation rowset, so we might as well use the same event. To begin, I created an Application Package and Class named GH_PERS_DET_EVT and PersonalDetailsTravelPrefs respectively. Next, we need to add a bit of PeopleCode to populate the appropriate Derived/Work record fields and rows. Identifying the proper buffer references requires a little bit of investigation. The key here is that Event Mapping PeopleCode has full access to the component buffer just like any other PeopleCode executing from a component event. Here is my PeopleCode:

import PT_RCF:ServiceInterface;

class PersonalDetailsTravelPrefs implements PT_RCF:ServiceInterface
   method execute();
   
private
   method AddStyle(&infld As Field, &inStyleName As string);
end-class;

method execute
   /+ Extends/implements PT_RCF:ServiceInterface.execute +/
   
   Local Rowset &rsLinks = GetLevel0()(1).GetRowset(Scroll.HCSC_TAB_DVW);
   
   &rsLinks.InsertRow(&rsLinks.ActiveRowCount);
   
   Local number &linkNbr = &rsLinks.ActiveRowCount;
   Local Row &linkRow = &rsLinks.GetRow(&linkNbr);
   
   Local Record &recWrk = &linkRow.HCSC_FL_WRK;
   Local boolean &isAccessibleMode = False;
   
   &linkRow.HCSC_TAB_DVW.ROW_NUM.Value = &linkNbr;
   
   %This.AddStyle(&recWrk.HCSC_GROUPBOX_02, "psa_vtab");
   
   /* initially hide counter and subtabs */
   &recWrk.HCSC_COUNTER.Visible = False;
   &recWrk.HCSC_EXPAND_ICN.Visible = False;
   %This.AddStyle(&recWrk.HCSC_GROUPBOX_03, "psc_hidden");
   
   &recWrk.HCSC_BTN_SELECT.Label = "Travel Profile";
   &recWrk.HCSC_BTN_SELECT.HoverText = "Travel Profile";
   
   REM ** generate the target URL for the new link;
   Local string &targetUrl = GenerateComponentPortalURL(%Portal, %Node, MenuName.GH_CUSTOM_FL, %Market, Component.GH_TRAVEL_PREF_FL, Page.GH_TRAVEL_PREF_FL, "");
   &recWrk.HCSC_BTN_SELECT.JavaScriptEvents = "href='" | &targetUrl | "'";
   
   If GetUserOption("PPTL", "ACCESS") = "A" Then
      &isAccessibleMode = True;
   End-If;
   
   If Not &isAccessibleMode Then
      
      /* set label image */
      &recWrk.HCSC_BTN_SELECT.LabelImage = Image.PS_EX_EXPENSE_M_FL;
      %This.AddStyle(&recWrk.HCSC_BTN_SELECT, "hcsc_image-maxtabheight");
      %This.AddStyle(&recWrk.HCSC_GROUPBOX_02, "psc_list-has-icon");
      
   End-If;
   
end-method;

method AddStyle
   /+ &infld as Field, +/
   /+ &inStyleName as String +/
   
   Local array of string &arrClass;
   
   REM ** Don't add classes that already exist;
   &arrClass = Split(&infld.FreeFormStyleName, " ");
   
   If &arrClass.Find(&inStyleName) = 0 Then
      &infld.AddFFClass(&inStyleName);
   End-If;
   
end-method;

Most of the code is self explanatory. It inserts a row into a rowset, and then sets appropriate values for each of the necessary fields. I was able to identify the relevant fields by investigating how Oracle populates this rowset. There is one line, however, that differs dramatically from Oracle's delivered code, and that is the line that sets a value for HCSC_BTN_SELECT.JavaScriptEvents. The delivered design for this Rowset uses FieldChange PeopleCode to Transfer to a different component on click. If you are using PeopleTools 8.55, you do not have access to map a handler to the FieldChange event. Likewise, even though 8.56 has support for mapping to the FieldChange event, early releases, such as 8.56.01 and 8.56.02 do not support mapping to FieldChange events in subpages. This rowset happens to reside in a subpage. As an alternative, this code generates a URL to the target component and then sets the HTML href attribute of the inserted row so that clicking the link opens a new component.

Note: the transfer method described here may not display the usual PeopleSoft warning message regarding unsaved data. A future iteration would leverage the FieldChange event, but not until after Oracle posts a fix for components with subpages.

The next step is to define a Related Content Service Definition. Although not necessarily related, the Related Content Framework contains all of the hooks necessary to implement Event Mapping. With that in mind, Oracle chose to make Event Mapping a subset of the Related Content Framework. To define a Related Content Service Definition, navigate to PeopleTools > Portal > Related Content Service > Define Related Content Service and add a new value. The ID you choose for your Related Content Service is not important. No one except an administrator will see the ID. Enter a user friendly service name and choose a URL Type of Application Class. It is this one piece of Metadata that will tell the Event Mapping Framework what code to invoke. When the Application Class Parameters group box appears, enter your package, path, and class name.

The final step is to map the Service Definition into a component event. Navigate to PeopleTools > Portal > Related Content Service > Manage Related Content Service. When you first land on this page, you may see a list of Content References containing Related Content. Immediately switch to the Event Mapping tab. On this tab, you will see an inappropriately labeled link with the text Map the event of the Application pages. Select this link. PeopleSoft will respond by displaying the typical enterprise menu in a tree structure. Since we are mapping to a Fluid component, and Fluid components don't exist in the menu, check the Include hidden Crefs checkbox. This will make the Fluid Structure Content item visible. Expand Fluid Structure and Content > Employee Self Service and then select Personal Details. Upon selection, PeopleSoft will present you with the Event Mapping configuration page. Notice that this page is divided into sections, with each section denoting a different definition type. The first group box, for example, is for Component events. Since we are mapping to the Component PostBuild event, it is this first group box we need to configure. From the Event Name drop-down list, select PostBuild. Next, select the service you created in the previous step. Since I created a service named GH_PERS_DET_TRAVEL, that is the Service ID selected in the screenshot below. The final metadata attribute, Processing Sequence, is very important. This attribute defines whether our code should run before or after Oracle's delivered code. In this case we are adding rows to the end of a Rowset and we don't want Oracle to do anything that would change the appearance or behavior of the rows we add. With that in mind, we choose Post Process, which tells the framework to run our code AFTER Oracle's delivered code. Save and test.

The above screenshot is from PeopleTools 8.56. Depending on your tools release, your page may appear slightly different.

After configuration, you should see a screenshot that resembles the following. Note the Travel Profile link at the bottom of the list.

Note: As previously mentioned, the Personal Details component contains links to several other components. To ensure that your component appears in the list on each of these other components, you also have to map your PeopleCode into the PostBuild event on each of those other components. Since these other components do not exist as tiles, you will find them directly in the Fluid Pages folder.

Special shout out to my friend Mike at Sandia National Labs, who demonstrated a similar approach at Collaborate 2017. Thank you, Mike, for the encouragement to persevere. I initially wrote the code and configurations for this blog post in December while working with some friends in UK. Unfortunately, due to inconsistencies in PeopleTools at that time, this solution did not work. With PeopleTools 8.55.15 being incredibly stable, this solution is now fully functional. I initially gave up hope for Event Mapping in Fluid. But seeing Mike present the exact same scenario renewed my faith.

Wednesday, June 21, 2017

New Window Bookmarklet

I am a "New Window" link junkie. I use that link ALL THE TIME! If it were possible to wear it out, mine would be worn out. I wish all PeopleSoft pages had the "New Window" link. For some reason, however, certain developers chose to remove it from specific PeopleSoft pages (such as Structure and Content). I'm sure there is a good reason... there just has to be. So seeing it missing from Fluid has been a significant struggle for me. I'm thankful for Sasank's Fluid UI - New Window Feature - Workaround customization. For quick access to a new window without customization, I have a Bookmarklet, which is a JavaScript fragment masquerading as a favorite (or bookmark). Here is the JavaScript:

(function() {
  var parts = window.location.href.match(/(.+?\/ps[pc])\/(.+?)(?:_\d+?)*?\/(.*)/);
  window.open(parts[1] + '/' + parts[2] + '_newwin/' + parts[3], '_blank');
}())

To add it to your bookmark toolbar, drag the following link into your link toolbar:

PS New Window

This solution is simple, but may not satisfy your requirements. This bookmarklet assumes you want to open a new window to the URL displayed in the address bar. That URL may or may not match the actual transaction. If you want a bookmarklet that opens a new window specifically targeting the current transaction, then try this bookmarklet:

(function() {
  var href = window.location.href;
  var parts = (!!frames["TargetContent"] ? !!frames["TargetContent"].strCurrUrl ? frames["TargetContent"].strCurrUrl : href : href).match(/(.+?\/ps[pc])\/(.+?)(?:_\d+?)*?\/(.*)/);
  window.open(parts[1] + '/' + parts[2] + '_newwin/' + parts[3], '_blank');
}())

To use it, drag the following link into your bookmark toolbar:

PS New Window

Special shout out to David Wiggins, who posted a similar bookmarklet on my Where is My New Window Link? post as I was writing this blog post.

Tuesday, June 20, 2017

Where is My New Window Link?

As PeopleSoft moves from Classic to Fluid, you have likely noticed the missing New Window link. Why is it missing? I can only speculate. When considering mobile, perhaps it makes sense to drop the New Window link. Mobile devices have limited screen real estate. Why waste it with a link you will likely never use on a mobile device? On a desktop, however, the New Window link is irreplaceable. So... what to do? How can you open a new window? You probably already know if you just open a new window without that special New Window link, your old window session will cease to exist. You know that you will receive the dreaded "... return to most recent active page..." message. Does that mean you can no longer have two active PeopleSoft windows? There is a work around that is documented in various places around the web. In short, the answer is to copy the current URL, open a new tab, paste the URL into the address bar, and then append _newwin to the site name. Before reviewing some examples, let's discuss what is going on and why this is necessary.

The PeopleSoft app server is stateless. App server connections are often pooled and used upon request. However, we know that component buffer state is stored somewhere. If not at the app server, then where? At the web server. As with any J2EE application, PeopleSoft uses web server sessions to store state (which is why load balancers must use sticky sessions, etc). The details here aren't exact, but metaphorical. PeopleSoft partitions web server session state into state blocks. A user may have multiple state blocks. The web server identifies the appropriate state block based on an identifier in the URL. When you click the New Window link, the link's URL pattern instructs the web server to generate a new state block. We can replicate the New Window link behavior by simply modifying a PeopleSoft URL. Let's review an example. Let's say you have a Fluid URL that looks something like this: http://hr.example.com/psc/ps/EMPLOYEE/HRMS/c/EL_EMPLOYEE_FL.HR_EE_ADDR_FL.GBL. The highlighted part between the servlet (psc) and the portal (EMPLOYEE) is the site name (ps). All we have to do is add _newwin to the site name. Accessing a URL such as http://hr.example.com/psc/ps_newwin/EMPLOYEE/HRMS/c/EL_EMPLOYEE_FL.HR_EE_ADDR_FL.GBL will instruct the web server to generate a new state block, perhaps something like http://hr.example.com/psc/ps_23/EMPLOYEE/HRMS/c/EL_EMPLOYEE_FL.HR_EE_ADDR_FL.GBL.

It certainly isn't as simple as the New Window link, but it is better than nothing. For a more permanent, user friendly method, take a look at Sasank's post Fluid UI - New Window Feature - Workaround.

Edit: I created this post because a lot of people I meet aren't familiar with the "New Window" trick. One very important caveat when working with classic pages: The URL in the header may not match the transaction URL. This appears to be tools release dependent. After PeopleSoft implemented "Partial Page Rendering" with a constant header and iframed content, the URL for the content area would change, but the URL in the address bar did not. What this means is simply copying the URL from the address bar and changing it to include '_newwin' will create a new state block, but that new window may point to different component from the original window.

Thursday, June 15, 2017

HIUG Interact 2017

Are you attending Interact 2017 this weekend? I will be leading a PeopleTools hands-on workshop on Sunday from 1:30 PM to 4:30 PM in Panzacola F-4. Because this is hands-on, please bring your laptop. All session activities will be completed over a remote desktop connection, so as long as you have a Remote Desktop Client and a full battery charge, you should be ready. In this session we will cover a variety of topics including classic global and component branding as well as fluid navigation and page development. I look forward to seeing you in Orlando!

Tuesday, May 23, 2017

Using CSS Frameworks with Fluid

Unlike PeopleTools Classic, which used almost-pixel-perfect layout, Fluid uses CSS for layout. The benefit of this pattern is flexibility. Unlike Classic pages, which only allowed for one layout, Fluid allows us to manipulate layout with CSS. I like CSS, so I see this as a positive. The more content Oracle development delivers in CSS, the more we can configure and transform the user experience. While I may love what I can do with CSS, it isn't the easiest language/technology to use. As a PeopleSoft developer with a significant amount of front-end development experience, I had this thought:

Could PeopleSoft developers avoid learning and writing CSS by using common, modern CSS frameworks to enhance PeopleSoft pages?

As with anything, the answer is, "Yes," but it isn't that easy. Here is the reason: PeopleSoft stylesheets apply style rules to HTML elements. And this makes sense. Review the most common CSS frameworks and you will see that they do the same thing (honestly, I would despise a CSS framework that didn't apply rules to HTML elements, but that is another story). Here is the problem: since both Fluid and and the framework style HTML elements, there are bound to be conflicts, and those conflicts often cause layout and display problems. Normally this would be fine, because selector specificity and order dictate who wins. The real problem is where the two frameworks have different, but related CSS declarations. The solution? A reset stylesheet. A reset Stylesheet will either reset conflicting Fluid CSS properties or conflicting your-favorite-CSS-framework properties. Now we have a solution. But, this solution has a serious problem: the point of using a CSS framework was to avoid writing CSS. After working through all of these conflicts, it is highly possible that you will write more CSS than you would have without the CSS framework. CSS frameworks are highly powerful and have value, but be careful to pick a CSS framework that uses class selectors only; no element selectors.

I have reviewed many CSS frameworks and it is hard to find one that just uses classes, no element styling. Oracle JET is a CSS framework that supports class-only styling. Oracle JET's Alta CSS stylesheets come in two flavors:

  • HTML element selectors
  • Class-only selectors

To incorporate Oracle JET with PeopleSoft Fluid:

  1. Download the latest class-only oj-alta-notag-min.css file (you can download the uncompressed version if you prefer). It is important that you grab the *-notag-*.css version. This is the one that uses class name selectors rather than HTML element selectors.
  2. Create a new Stylesheet definition and paste the contents of the oj-alta-notag CSS file into the new Stylesheet definition.
  3. Use the AddStylesheet PeopleCode function in a Page Activate event to add the new free form stylesheet to a page.
  4. Add Oracle JET class names to fields on your page.

1. Download the latest oj-alta-notag-min.css file

You don't need to "download" the file, just access its content. We will copy and paste the content into PeopleSoft in the next step. The file is available in raw form on GitHub here.

2. Create a new Stylesheet definition

After logging into your PeopleSoft development instance as an administrator, navigate to PeopleTools > Portal > Branding > Branding Objects. Switch to the Stylesheet tab and select the Upload Style Sheet Object. This will open a dialog where you can paste the CSS from step 1. Be sure to give the Stylesheet an appropriate name. I chose ORACLEJET_FF. Uploading a Stylesheet in this manner creates a managed Stylesheet definition that is accessible from Application Designer, and mixed with all of the other App Designer Stylesheet search results, so naming is important. Be sure to use your site-specific prefix. PeopleTools best practice recommends using the _FF suffix as well to denote this Stylesheet as a Free Form Stylesheet, which differs from the more traditional structured PeopleTools Stylesheet.

3. Use the AddStylesheet PeopleCode function in a Page Activate event

To any page that will use Oracle JET styling, open the PageActivate event and add PeopleCode that looks something like:

AddStyleSheet(StyleSheet.ORACLEJET_FF);

4. Add Oracle JET class names to fields on your page

Now open your Fluid page in Application Designer and identify the field that is supposed to use an Oracle JET class. Open the field's properties and switch to the Fluid tab. In the Fluid tab, set the Default Style Name to an Oracle JET style class. My favorite Oracle style classes to use with Fluid are oj-flex and oj-flex-item because they allow me to use CSS Flex Layout with Groupbox containers to ensure proper responsive design across a variety of form factors.

I think it is safe to say the most popular CSS framework in the world is Bootstrap. A common question developers ask me is, "Can I use Bootstrap with PeopleSoft Fluid?" The answer, of course is, "Yes." While I prefer Oracle JET because of its no-tag alternative, I have friends that successfully use Bootstrap with PeopleSoft Fluid pages. With PeopleSoft, all things are possible!

Thursday, February 09, 2017

Alliance 2017

We are in the final countdown for Alliance 2017. I am really excited about this conference. It is such a great opportunity to meet up with old friends as well as make new ones. The Alliance session content and delivery is extremely high caliber. HEUG is a very engaged community. The MGM is a pretty amazing facility as well.

At GreyHeller our week starts with an amazing Monday workshop. On Monday, February 27th from 10:00 AM to 2:30 PM, Larry Grey and I will be hosting a pre-conference workshop titled Advanced PeopleTools Development Workshop with Jim Marion (session 4378). Our objective is to give you hands on experience with all of the new PeopleTools including Fluid and the Event Mapping Framework. But wait, there's more... Fluid itself is a new development paradigm with a lot of flexibility. In this session you will learn how to use CSS and JavaScript to further enhance the PeopleSoft user experience. For more details and registration, visit the Monday Workshops page on the Alliance conference site.

On Tuesday morning at we join our partner MODO LABS at 8:30 AM to present the session A Student for Life - Engaging prospective, new, current, and past students has never been easier. In this session you will see how MODO LABS partnered with GreyHeller makes it trivial to embed PeopleSoft content in a native, secure user experience giving users access to native, on-device capabilities such as maps, notifications, etc.

On Tuesday, February 28th from 09:45 AM to 10:45 AM, our friends from the University of Massachusetts will be sharing about their experience mobilizing and modernizing the Student Center (session 4036) at their UMass Boston, Dartmouth and Lowell campuses using our PeopleMobile™ product. It really is amazing how our product transforms the PeopleSoft user experience. Definitely a "must see."

On Tuesday, February 28th from 1:15 PM to 3:15 PM, Larry and I will be leading the PeopleSoft Cloud to Ground workshop – Cloud Adoption Strategies and Best Practices (session 4381). In the ERP space, Hybrid "is the new black." There are a lot of great cloud enhancements to a traditional ERP. Anyone thinking about implementing cloud is also thinking about backend data integrations. But what about the user experience? You don't have to settle for a disjointed user experience. In this session, Larry and I will show you how your organization can integrate the UX layer into a single, common user experience.

On Thursday, March 2nd at 9:15 AM, my friend Felicia Kendall from UCF will be sharing about their highly publicized breach (including costs) and their experiences with securing PeopleSoft after a highly publicized breach. This should prove to be a very valuable session. The session is titled University of Central Florida: Post-breach Mitigation & Prevention Strategy (session 4108).

While attending Alliance, be sure to wander through the demo grounds. Our booth (#301) will be right beside the Oracle booth. I'm looking forward to wandering through and visiting with my friends from Oracle, Ciber, Deloitte, Gideon Taylor, Intrasee, Smart ERP, Accenture, Presence of IT, MODO LABS, Huron, Sierra-Cedar, and many more.

See you on the floor!

Monday, October 24, 2016

Cloud to Ground Mashup Webinar

At 11:00 AM Pacific on Tuesday, October 25th (tomorrow), I have the privilege of talking about Cloud and on-premise (ground) integration. Whether cloud to cloud, cloud to ground, or ground to ground, integration is probably one of the most difficult aspects of any implementation. Integration comes in two flavors:

  • Back-end
  • Front-end

Back-end integration is the most common. Back-end integration involves integrating data between two systems either for processing or presenting a common user experience.

Front-end integration is about combining the user experience of two separate applications to create a common user experience. I often find that I can eliminate some of the back-end integrations if I can appropriately mashup front-end applications. In this webinar you will learn enterprise mashup strategies that allow you to present a seamless user experience to your users across cloud and ground applications. No modifications. Just tailoring and configuration.