Thursday, July 27, 2017

Event Mapping: FieldChange

In my post Event Mapping: Extending "Personal Details" in HCM I noted that PeopleTools 8.56 FieldChange event mapping didn't work with fields from subpages. I opened a bug with MyOracle Support and Oracle sent me a fix to test. The fix works great! Now that I have FieldChange Event Mapping working for that prior scenario, I wanted to share how that Personal Details scenario would change. First, I would remove the JavaScriptEvents line. Specifically, these two lines:

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 | "'";

Next, I create an Application Class to hold my new FieldChange event mapping PeopleCode:

import PT_RCF:ServiceInterface;

class PersonalDetailsRowClick implements PT_RCF:ServiceInterface
   method execute();

method execute
   /+ Extends/implements PT_RCF:ServiceInterface.execute +/
   When = 11 /* Travel Preferences  */
      Transfer( False, MenuName.GH_CUSTOM_FL, BarName.USE, ItemName.GH_TRAVEL_PREF_FL, Page.GH_TRAVEL_PREF_FL, %Action_UpdateDisplay);      

OK... the code is short and easy to read. Basically, it transfers to another component if a specific condition is met. I believe the confusing part is that condition. The problem that I see is that this code is missing context. Why does that condition exist? What does it mean? Where did that number 11 come from? What is HCSC_TAB_DVW.ROW_NUM? This is a problem I have with Event Mapping. My code is just a fragment with no context. Referring back to the code from Event Mapping: Extending "Personal Details" in HCM, we see that HCSC_TAB_DVW.ROW_NUM is the grid row number. So basically, if the current row clicked is row number 11, then transfer to the specified component.

When writing Event Mapping PeopleCode, it is very important that you understand the base code, the code you are enhancing. The delivered FieldChange event uses an Evaluate statement to transfer based on an identifier that happens to be stored in HCSC_TAB_DVW.ROW_NUM. Oracle is using a row number to choose the target component on click (FieldChange). I followed the same pattern and that pattern led me to number 11. But what if Oracle adds another row? Suddenly my code (or Oracle's code—order matters) breaks. Perhaps I should have used an artificial number such as 100 assuming Oracle would never add 100 items to that "collection"? I can think of few other ways I may have written the initial code that would be easier to follow. For example, what if the code that establishes the rowset also pushed the transfer parameters (menu, bar, item, etc) into fields in the derived work record? Anyways... back to Event Mapping...

The remainder of the configuration is the same as Event Mapping: Extending "Personal Details" in HCM. I create a service definition and map that service definition to the FieldChange event of HCSC_FL_WRK.HCSC_BTN_SELECT. Next step is to choose the Processing Sequence: Pre or Post. I don't have a good answer here. Either way your code will run. If you choose Pre and Oracle adds an eleventh item, then Oracle's code will win. If you chose Post, then your code will win.


Charandeep Singh said...

This is great Jim, I wasn't aware we can could do this. If we like to add new component in-between other component (not at end), I don't think this could work then? But I do see many other places where we can use this. Thanks!

Jim Marion said...

@Charandeep, in between, no, but first, yes. Your code would run first and Oracle's would never run. So if there was some delivered code you still wanted to run, you would have to copy it into your event handler and run yours first. The transfer would ensure that Oracle's code didn't run.

Charandeep Singh said...

Jim, good to hear there is workaround, will check it out. Thanks!

Charandeep Singh said...

Jim, One off-topic question, I have subscribed to your blog's RSS via Feedly, but when we click on any post, it doesn't show post body in its preview, instead it takes to post's link. Have you did something or is there an issue with RSS XML?

Jim Marion said...

@Charandeep, I haven't changed anything on my end. This URL appears to show the full post in the Atom feed:

Neil Yetman said...

Hi Jim, Re your worry about supporting components with these Event Mapping configurations, how about this for an idea? You can add a global branding object (JavaScript) that calls back to an iScript to query the Event Mapping configuration. And if there are Event Mappings for the component, then list them to the browser's console. That might give people supporting PS systems some place to start to check/rule out Event Mapping.


Jim Marion said...

@Neil, good point. I like the idea of creating tools to help identify Event Mapping.

Tom Williams Jr. said...

Hi Jim.

Great post and you got me thinking about taking advantage of the Event Framework. One challenge that recently came across my desk was to inject a prompt of some sort into a Configuration Page save event so that a user making those changes could provide comments that described why they needed to make the change. We would then audit these updates and make sure they made it into our weekly change control communications.

I've tried this using the "Prompt" command and failed due to it being a think time event. I've tried DoModalComponent and DoModalXComponent but they require a shared work record and I want my package to be able to freely bolt onto any configuration page without customization.

If you have any suggestions that might get the brain power flowing in the right direction I'd appreciate it.

I'd really like to use the Event Mapping Framework to accomplish this task.

Thanks in advance...

Tom Williams Jr.

Jim Marion said...


Great question. Basically, you have to avoid any function that causes a request/response cycle during certain events. There is no way to capture feedback from the user after the save button hits the web server and before the save event (a smart ServletFilter could do it). I believe this includes modal function calls.

Anther challenge is that Event Mapping can't add or remove fields from the component buffer (although it can SQLExec).

If I were going to make this happen with Event Mapping, I think I wouold dig into the Save event JavaScript and MonkeyPatch save. Remember, it isn't just a button click, but also a key combination (Alt-1?). This allows you to inject JavaScript into the save processing cycle. That JavaScript would do one of 2 things: 1 it might add a hidden input field to the posted form to retrieve in my Event Processing PeopleCode using %Request or 2 it might send the contents to an iScript for saving.

Neither of these is foolproof. The only true logic is the server's logic. Anytime you push logic onto the client, a smart (or devious) user can bypass that logic and send requests to the web/app server. As you have seen, however, you can't ask the user for input from Save event PeopleCode.

Tom Williams Jr. said...

Thank you Jim for your feedback.

I get a little concerned with the MonkeyPatch save from a supportability and security point of view but it would be fun just to see if I could do it in a PUM installation :).

I think I can still use the Event Mapping to log changes. I may even be able to loop through the buffer generically and log each field that had a change to a long object. I think this may get us 75% of the way there. It is unfortunate though that we cannot log a users intent. It will be interesting to see where they take the Event Mapping in the next tools releases.