Friday, August 26, 2011

GenerateComponentContentURL() Fail

We have a custom PeopleSoft module that handles requests from another web application. After authentication, this web application initially forwards the request to an iscript that, after identifying the user, redirects her to the appropriate home page. The redirect URL is created using the built-in PeopleCode function, GenerateComponentContentURL(). Here is an example:
%This.approverHomePage = GenerateComponentContentURL(%Portal, %Node, MenuName.MY_MENU, "GBL", Component.MY_HOME, "", "");
 ...
%Response.RedirectURL(&userIdentity.getHomePageURL());

This was working fine until the web application was updated. At that point, the redirect started to fail silently. After a lot of false starts, I determined that GenerateComponentContentURL was returning an empty string, but I could not figure out why.

Long story short, the newly-configured web application was using a URL for the iscript that had an invalid node. The url was:

https://myapp.com/psc/dbname/EMPLOYEE/PSFT/s/WEBLIB_CUSTOM.NAV_FUNCTIONS.FieldFormula.IScript_Navigation

but should have been:

https://myapp.com/psc/dbname/EMPLOYEE/PSFT_HR/s/WEBLIB_CUSTOM.NAV_FUNCTIONS.FieldFormula.IScript_Navigation

This caused %Node to return PSFT, an invalid node. One would think that GenerateComponentContentURL would throw an exception in this case, but it just returned an empty string. I also find it interesting that even with an invalid node in the URL, the iscript still ran.

Thus is life customizing PeopleSoft.

Thursday, August 11, 2011

Custom Buttons on Modal Pages

There are lots of good reasons to use modal pages and components in PeopleSoft. However, one thing you sacrifice is control, especially if you use the delivered Save and Cancel buttons that PeopleTools adds to your pages. There is nothing to stop you from adding your own buttons to the pages, but if you do the delivered buttons are still there, perhaps confusing the user. At least for the modal component, there is a solution. I tried disabling the toolbar in the component internet properties, but the buttons were still there.




Then a co-worker (thanks Giri) pointed out that if you actually uncheck the Save and Cancel checkboxes, the buttons are not shown. Wow. He's right. Problem solved. Is this a bug? Probably. Btw, we use PeopleTools 8.49.


Monday, August 8, 2011

Get Rid of Unwanted Save Warning

Have you ever had a component that gave you unwanted Save Warnings. I recently had to deal with this problem on a page that had a custom button that among many other things called doSaveNow() and then a transfer. Despite calling doSaveNow, the transfer resulted in a Save Warning. I traced the PeopleCode, but could not find any change between the save and transfer that should result in the warning. Frustrated, I searched online for a solution. I found an undocumented PeopleCode function designed for this situation - SetSaveWarningFilter( True).

Calling SetSaveWarningFilter with True input parameter causes the subsequent page to ignore the SaveWarning. It's not perfect because user is shown the page that would display the Save Warning, but then routed to the next page as if the user clicked the cancel button on the Save Warning dialog. In our situation this was good enough.

Tuesday, April 12, 2011

How Select Options are Set on a PeopleSoft Page


I just learned something new about how PeopleSoft pages work. We are using PeopleTools 8.49.29. Your mileage may vary.

It appears that if more than one Select (dropdown) have the same options, PeopleTools puts the options into a JavaScript array, then populates the options with JavaScript after the page loads. I learned this the hard way because I had a jQuery function that ran on page load and uses the select values. It was not working right because the select values were not set when the script ran.

The function that populates the options and sets the selected values is setSelectElemOptions_win0(). I solved my problem by creating a proxy for this function and adding a call to my function. This ensures that my function runs after the Select options are set. See my previous post on how to create a proxy.


Here's the code.


// proxy the setSelectElemOptionsProxied function to ensure Select Options
// are set before totalHours is called on page load
var setSelectElemOptionsProxied;
(function() {
// proxy setSelectElemOptionsProxied
setSelectElemOptionsProxied = setSelectElemOptions_win0;
setSelectElemOptions_win0 = function() {
setSelectElemOptionsProxied(); // call the original function
totalHours(); // my function
};
})();




Thursday, February 17, 2011

Using JavaScript in PeopleSoft: Creating your own dialog boxes

In previous posts, I've described how I have been using JavaScript to enhance our PeopleTools 8.49 application. I've covered calculations and validation, and showed how you can proxy "delivered" PeopleSoft JavaScript to enhance your pages.

In this post I'll show how we are replacing the Peoplesoft confirmation (Yes/No OK/Cancel) message box with our own slick modal dialog boxes. I'm sure you'll agree with me that the bare, ugly pages produced by using PeopleCode messageBox() for confirmation aren't up to modern web standards. I went searching for a way to replace it. I found jQuery UI dialog. jQuery UI is a jQuery plugin designed to provide JavaScript UI elements that complement jQuery. Dialog provides a robust set of tools for creating flexible client-side dialog boxes. Here's an example of a typical OK/Cancel confirmation dialog produced by Dialog.


Dialogs are typically defined statically and called by dynamic Javascript. I've settled on a pattern that seems to work well with PeopleSoft. I define the dialog based on an empty <div>, but set most of the properties, including the html displayed in the dialog, from the event-driven code and bind variables.

Here's a typical definition.

   var $confirmDialog = $('<div></div>').dialog({
title: '%BIND(:10)',
autoOpen: false,
resizable: false,
modal: true,
minHeight: 400,
minWidth: 350,
buttons: {
OK: confirmOK,
Cancel: confirmCancel
}
});


A few things to notice. The dialog html is an empty div (<div></div>). The actually html shown in the dialog is set when it is called from a function. I'm setting the dialog title with a bind variable. autoOpen is set to false; otherwise the dialog would open when the page is loaded. The button actions are defined as named functions. They can be anonymous functions, but I find that using named functions here is cleaner, more flexible and can be reused. In many cases, I replace the actions when I call the dialog.

Here' s a function that calls the $confirmDialog:

function confirmIt(msg){ //msg must be formated as html
$confirmDialog.dialog('option' , 'height', 450);
$confirmDialog.dialog('option' , 'width', 400);
$confirmDialog.dialog('option' , 'buttons' , {OK: confirmSubmit, Cancel: closeDialog} );
$confirmDialog.html(msg);
$confirmDialog.dialog('open');
}


In this case, I'm replacing the button functions with:

// OK button function for confirm dialog
var confirmSubmit = function() {
$(this).dialog('close');
$('#TC_DERIVED_CONFIRMED').val('Y'); //hidden field
return submitProxied.apply(window, localArgs); // continue submit process
};

var closeDialog = function(){
confirmed = false;
$(this).dialog('close');
}


confirmSubmit:
  • closes the dialog box; 
  • sets a hidden field that I've added to the page with JavaScript following the PeopleTools naming convention that I can query in PeopleCode to confirm that the user has confirmed the submit on the client-side; 
  • passes the processing to the standard PeopleTools submit processing JavaScript. (I describe how this works in a previous post.)
closeDialog sets global confirmed to false and closes the dialog. This, in effect, cancels the submit and leaves the user on the original page.

You can define as many buttons as you wish, each with its own function.

The Validation Chain

This works fine for a single-step confirmation - something like "Submit your timesheet? You will not be able to make further changes. OK Cancel." But when you call a dialog box you are turning over processing to the dialog code. Unlike a PeopleSoft messageBox, it never returns a value to the calling code. So, what if you need to process several validations and confirmations? To handle this, I use a technique I call the validation chain. In the validation chain, each step is a function that can stop the processing or call the next step until the chain is complete.

Here's an example.

   function myFunction () {
       ...
       //validationChain is declared global
validationChain = [validateInOut,validateZeroHours,validateTimeOnHoliday,validateSubmit];

       return nextValidation();

       ...
  }

   // call the next validation in the chain
   function nextValidation() {
var next = validationChain.shift();
var nextUndefined = next == undefined;
if (!nextUndefined) {
           return next()
};
      return true;
}


First, we set up a global array of validation/confirmation functions; the validation chain. Then we call nextValidation().

nextValidation removes the first function from the chain and stores it in variable next. Then, if the function is defined, it is called.

Now each function can do validation or confirmation including calling a dialog box. To stop the process the function returns false. To continue, the function, or the dialog it calls, calls nextValidation(). Here's an example validation function that can be added to the validation chain:

 // User confirms time on holiday  
function validateTimeOnHoliday() {
if (timeOnHoliday()){
var msg = '%BIND(:2)'.
replace('%1',holidayDateString);
$confirmDialog.dialog('option' , 'buttons' , {OK: confirmContinue, Cancel: closeDialog} );
$confirmDialog.html(msg);
$confirmDialog.dialog('open');
return false; // we'll only get here if the user cancels
} else {
return nextValidation()
};
}

// Continue button function for validation chain dialog
var confirmContinue = function() {
$(this).dialog('close');
return nextValidation();
}

// Close the dialog without taking any action - this will end the validation chain
var closeDialog = function(){
$(this).dialog('close');
}


Now you can call any number of validations and confirmations before submitting the page to the server.

I hope you have found my JavaScript blog entries helpful. If you have your own ideas on how to use JavaScript to improve PeopleSoft applications, please let me know.