Friday, March 21, 2014

Collaborate 2014 Schedule

I am just getting caught up after HEUG Alliance. What a great conference! As always, it was a lot of fun visiting with customers, partners, and colleagues. I heard some amazing stories and learned a few new tricks. Now it is time for the next conference: Collaborate 2014 is only a couple of weeks away. I love hearing your stories and challenges. Hunt me down and share your stories with me. Besides the demo grounds, here are two places you will find me:

  • Tuesday, 8 Apr 3:00 PM-4:00 PM So You Think You Know PeopleSoft? Do You Know the PeopleSoft Interaction Hub? – You Own It !, session ID 109490, Level 4, Lando 4301B
  • Thursday, 10 Apr 08:30 AM-09:30 AM PeopleTools Developer: Tips and Techniques, session ID 108600, Level 4, Marcello 4401A

I fly out Thursday night, but if I can make it, I plan to attend Mike Doyle's session Advanced PeopleSoft Development Techniques, which contains examples based on my book PeoleSoft PeopleTools Tips & Techniques.

Monday, February 24, 2014

Alliance 2014 Agenda

It is hard to believe it has been a year since the HEUG Alliance 2013. We had a lot of fun in Indianapolis. I have been reviewing the 2014 session lineup and it looks pretty exciting. I must say that I am very pleased to see several sessions on REST, iScripts, and User Experience. With Alliance 2014 just a couple of weeks away, I wanted to make sure I posted my schedule:

  • Monday at 1:45 PM - 2:45 PM 33718 PeopleTools Developer: Tips and Techniques in Room 312-317
  • Tuesday 11:00 AM - 12:00 PM Meet the Experts

I also plan to spend some time in the PeopleSoft demo grounds. Please stop by and visit with me or one of my colleagues. We would love to say hello and show you some new ideas for configuring PeopleSoft applications.

If you are presenting at Alliance, please feel free to promote your session in the comments below.

Tuesday, December 17, 2013

Quest Technology Series PeopleTools Presentation

If you are a Quest member and missed my PeopleTools Tips and Techniques presentation last week, you can watch the recording on Quest's site at

An Interview with Jeff Robbins

At OpenWorld 2013, I had the opportunity to catch up with Jeff Robbins and ask him some questions about PeopleSoft's User Experience. You can watch the video here:

Watch more videos from Oracle's User Experience team on the oracleuseableapps YouTube channel.

Friday, November 15, 2013

10 PeopleSoft Interaction Hub CSS Tricks

If you have been to one of my PeopleSoft user experience sessions, you have likely seen a good handful of interesting CSS ideas in my designs. Ever wanted to implement some and need a few pointers? Here is a little Q&A that I hope you find useful.

Q: How do you keep a PeopleSoft or content provider stylesheet from overriding your Interaction Hub (portal) pagelet styles?

A: Higher specificity. I make sure my styles have a more specific CSS selector than the delivered CSS selector. This is actually pretty easy because the delivered CSS selectors for pagelet elements (ptpageletheader, ptpageletbody, ptpgltlabel, etc) just use class names. To make your selector more specific, just include .ptpagelet in front of your selectors. Here is a sample from one of my free formed stylesheets:

#ptpglts .ptpagelet .ptpageletheader {
  border-radius: 10px 10px 0 0;

The delivered selector is .ptpageletheader. I make my CSS selector more specific by adding #ptpglts .ptpagelet to the selector.

Q: How did you make the pagelets on your green/grass theme have a semi-transparent background?

A: There are actually a couple of ways to accomplish this. I created this theme back before rgba support in IE, so the approach I took was to create a 2x2 pixel PNG image with a semi-transparent background. I then set that to be my pagelet's background. Here is an example:

#ptpglts .ptpagelet td.ptpageletbody {
  background: url("opacity-bg.png") repeat scroll 0 0 transparent;

Today we can reduce our downloads and accomplish this much more easily using the rgba color syntax:

#ptpglts .ptpagelet td.ptpageletbody {
  background: none repeat scroll 0 0 rgba(255, 255, 255, 0.5);

Q: How did you create rounded corners for your pagelets?

A: I used the border-radius CSS attribute:

#ptpglts .ptpagelet .ptpageletheader {
  border-radius: 10px 10px 0 0;

I set the top left and right radius on the ptpageletheader class and the bottom left and right radius on the ptpageletbody CSS class.

Q: How do you make some pagelets have a transparent background while others have a color or image as a background?

A:Some pagelets, such as the accordion, look better with no header, border, or background. Through Pagelet wizard (or the new 8.53+ Pagelet Branding), you can hide the border and header, but you can't change the background. The technique I use is to first create, save, and add the pagelet to a homepage. Next, I find the pagelet's ID in HTML (firebug is very helpful for this). With the ID in hand, I write a custom CSS selector, setting the background to transparent. Here is an example:

#ptpglts #ADMN_COMPANY_DIRECTORY_IMG.ptpagelet td.ptpageletbody {
  background: transparent;

Q: How do you make the drop-down menu's bar semi-transparent?

A: Set a semi-transparent background on #pthnavcontainer. You can either use a semi-transparent image or RGBA colors. Here is an example. As with other examples, use a highly qualified selector (specificity) to ensure your selector wins over the delivered CSS selector.

body.PSPAGE #pthnavcontainer {
  background: none repeat scroll 0 0 rgba(0, 0, 0, 0.2);

Q: How do you set a background image for the entire PeopleSoft page?

A: Set a background image on .PSPAGE like this:

body.PSPAGE {
  background: url("background-photo.jpg") repeat fixed 0 0 transparent;

Make sure that:

  • Your background image size extends far beyond the expected page size so the image doesn't repeat.
  • You use the fixed attribute so your image doesn't have to extend passed the scrollable area of a page.
  • Your chosen background doesn't make transactions difficult to read.

Q: How do you make the main header area's background show through the transaction area?

A: There are a couple of ways to accomplish this. If you are OK with a semi-transparent appearance, then the easiest way is to add the following to your role based branding header's CSS:

#ptifrmtgtframe {
  opacity: 0.9;

This will make the transaction area semi-transparent. This includes the buttons, text, and every other element within the transaction area. A value of .9 seems to be opaque enough to view the entire transaction area while still allowing a small amount of the background to show through. Just for fun, use Firebug or Chrome tools to try smaller values. Once you get down to .5, the transaction area should be noticeably transparent.

Q: How do you set a background for just the pagelet region of a homepage?

Set a background image on the #ptpglts element. Here is an example:

.pspage #ptpglts {
  background: url("my-favorite-background.jpg") repeat fixed 0 0 transparent;

Q: How do I center the pagelet area and reduce the size to something like 1024 pixels wide?

A: As long as you are using a browser other than IE 8 (PeopleSoft 8.53- requires IE Quirks mode), you can do this using the margin auto CSS centering technique. Here is an example:

.pspage #ptpglts {
  margin: 0 auto;
  width: 1024px;

Q: How do you round the right top and bottom corners of the SES scope drop-down in the global search area of the header?

A: The SES search scope drop-down has the ID selsrchgrp. Here is some CSS that rounds the right side of the search scope drop-down. Since this drop-down is paired to a text field that has the same height, I only round the right side, not the left side.

.pspage #selsrchgrp {
  border-radius: 4px 0 0 4px;

Thursday, October 10, 2013

Thursday, September 26, 2013

Using External Content in the FSCM 9.2 Links Pagelet (WorkCenters)

FSCM 9.2 comes with some great new WorkCenters. One of the features of the new FSCM WorkCenters is the configurable Links pagelet. With the links pagelet, you can add and remove links to information related to the WorkCenter's business process. One of the great features of the Links pagelet is that it allows you to set the starting page of a delivered WorkCenter. A current limitation of the Links pagelet is that it does NOT allow you to open external content in the WorkCenter's TargetContent area. To say it another way, you can add external content to the Links pagelet, but that external content opens in a new window. As usual, however, the only real limitation is imagination. Here is the method I developed that allows me to add external content to the Links pagelet and have it open in the TargetContent area: an iScript that redirects to the external content. I then register this iScript as a CREF and add it as a Link. The Links pagelet thinks the content is local, so it opens the external content in the TargetContent area. Here is the iScript:

Declare Function SpecifyPortalOpen PeopleCode FUNCLIB_PTPP.PTPP_PORTALR FieldFormula;

Function IScript_ContentRedirect()
   Local ApiObject &portal;
   Local ApiObject &cref;
   Local string &url = "";
   Local string &portalName = %Request.GetParameter("PORTAL");
   Local string &crefName = %Request.GetParameter("CREF");
   &portal = SpecifyPortalOpen(&portalName);
   &cref = &portal.FindCREFByName(&crefName);
   If (&cref <> Null) Then
      If (&cref.Authorized) Then
         &url = &cref.AbsoluteContentURL;

Here is how you use it:

  1. Create a CREF for your external URL
  2. Create a new CREF for the iScript. In the additional parameters section of the CREF, add PORTAL=EMPLOYEE&CREF=YOUR_CREF_NAME
  3. Update the new iScript CREF's security to match the external content CREF's security.

I use this technique with both OBIEE dashboards and Taleo.

Monday, September 09, 2013

Writing to stdout and stderr Take II

A few years ago I wrote a post that describes how to print text to the App Engine output file, bypassing the verbose MessageBox statement with its text length limitation (AppEngine Output Tricks, Reporting, Logging, Etc). I recently employed that same technique for logging Taleo Connect Client output when run from an App Engine. Looking at the code from those old println methods, I noticed a couple of inefficiencies:

  • Each invocation of println initializes the same JavaObject variables resulting in wasted CPU cycles and wasted memory
  • The two functions, println_to_stderr and println_to_stdout, are nearly identical which violates the DRY principle

The solution to both of these problems is inherent in Application Classes. We can solve the first issue by maintaining state inside the App Class with private instance variables. The DRY violation could be solved through properly implemented composition (preferred) or inheritance.

Here is an alternate implementation of the println_to_stdout method that uses an App Class to maintain state between invocations:

class StdoutWriter
   method println(&message As string);
   instance JavaObject &printlnMethod_;
   instance JavaObject &outputStream_;

method println
   /+ &message as String +/
   /+ Extends/implements NAA_STDIO:IOWriter.println +/
   REM ** Lazy initializtion to ensure initialized between invocations;
   REM ** Need a local copy of member for "None" test;
   Local JavaObject &outputStream = &outputStream_;
   If (None(&outputStream)) Then
      REM ** NOTE: this is the only difference between StderrWriter and StdoutWriter;
      &outputStream_ = GetJavaClass("java.lang.System").out;
      Local JavaObject &stringClass = GetJavaClass("java.lang.Class").forName("java.lang.String");
      Local JavaObject &printStreamCls = &outputStream_.getClass();
      Local JavaObject &printlnArgTypes = CreateJavaObject("java.lang.Class[]", &stringClass);
      &printlnMethod_ = &printStreamCls.getDeclaredMethod("println", &printlnArgTypes);
   &printlnMethod_.invoke(&outputStream_, CreateJavaObject("java.lang.Object[]", &message));
   rem ** I didn't find flushing necessary, but here is where you would flush the buffer if desired;
   rem &outputStream_.flush();

Notice that I used lazy initialization and only persist two JavaObject variables. The life of a JavaObject variable is potentially shorter than most PeopleCode variables. The PeopleSoft runtime can persist many types of PeopleCode variables across requests (events, think time functions, etc). This is not the case with Java variables. Lazy initialization and re-initialization ensures those Java variables always have a value. I only persist two variables rather than the seven from the original println_to_stdout function because we only need two of those variables for subsequent invocations.

About the DRY principle violation... I chose not to solve it. The number of lines required to create another app class (for composition or inheritance) was about the same as the number of duplicate lines. If I had multiple targets besides stderr and stdout, then creating a class structure to contain this redundant code would make sense. In this case the clarity seemed worth a little redundancy.

So how do you use it? Assuming you put this class in an Application Package named JJM_STDIO, you would call it like this:

import JJM_STDIO:StdoutWriter;

Local JJM_STDIO:StdoutWriter &out = create JJM_STDIO:StdoutWriter();

&out.println("This could be a very long line of text read from some process output that would exceed the maximum length for MessageBox");