Thursday, July 24, 2014

Unlimited Session Timeout

There are a lot of security admins out there that are going to hate me for this post. There are a lot of system administrators, developers, and users, however, that will LOVE me for this post. The code I'm about to share with you will keep the logged in PeopleSoft user's session active as long as the user has a browser window open that points to a PeopleSoft instance. Why would you do this? I can think of two reasons:

  • Your users have several PeopleSoft browser windows open. If one of them times out because of inactivity at the browser window level, then it will kill the session for ALL open windows. That just seems wrong.
  • Your users have long running tasks, such as completing performance reviews, that may require more time to complete than is available at a single sitting. For example, imagine you are preparing a performance review and you have to leave for a meeting. You don't have enough information in the transaction to save, but you can't be late for the meeting either. You know if you leave, your session will time out while you are gone and you will lose your work. This also seems wrong.

Before I show you how to keep the logged in user's session active, let's talk about security... Session timeouts exist for two reasons (at least two):

  • Security: no one is home, so lock the door
  • Server side resource cleanup: PeopleSoft components require web server state. Each logged in user session (and browser window) consumes resources on the web server. If the user is dormant for a specific period of time, reclaim those resources by killing the user's session.

We can "lock the door" without timing out the server side session with strong policies on the workstation: password protected screen savers, etc.

So here is how it works. Add the following JavaScript to the end of the HTML definition PT_COMMON (or PT_COPYURL if using an older version of PeopleTools) (or even better, if you are on PeopleTools 8.54+, use component and/or role based branding to activate this script). Next, turn down your web profile's timeout warning and timeout to something like 3 and 5 minutes or 5 and 10 minutes. On the timeout warning interval, the user's browser will place an Ajax request to keep the session active. When the user closes all browser windows, the reset won't happen so the user's server side session state will terminate.

What values should you use for the warning and timeout? As low as possible, but not so low you create too much network chatter. If the browser makes an ajax request on the warning interval and a user has 10 windows open, then that means the user will trigger up to 10 Ajax requests within the warning interval window. Now multiply that by the number of logged in users at any given moment. See how this could add up?

Here is the JavaScript:

(function (root) {
    // xhr adapted from http://toddmotto.com/writing-a-standalone-ajax-xhr-javascript-micro-library/
    var xhr = function (type, url, data) {
        var methods = {
            success: function () {
            },
            error: function () {
            }
        };

        var parse = function (req) {
            var result;
            try {
                result = JSON.parse(req.responseText);
            } catch (e) {
                result = req.responseText;
            }
            return [result, req];
        };

        var XHR = root.XMLHttpRequest || ActiveXObject;
        var request = new XHR('MSXML2.XMLHTTP.3.0');
        request.open(type, url, true);
        request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        request.onreadystatechange = function () {
            if (request.readyState === 4) {
                if (request.status === 200) {
                    methods.success.apply(methods, parse(request));
                } else {
                    methods.error.apply(methods, parse(request));
                }
            }
        };
        
        request.send(data);
        return {
            success: function (callback) {
                methods.success = callback;
                return methods;
            },
            error: function (callback) {
                methods.error = callback;
                return methods;
            }
        };
    }; // END xhr


    var timeoutIntervalId;
    var resetUrl;

    /* replace warning message timeout with Ajax call
     * 
     * clear old timeout after 30 seconds
     * macs don't set timeout until 1000 ms
     */
    root.setTimeout(function () {
        /* some pages don't have timeouts defined */
        if (typeof (timeOutURL) !== "undefined") {
            if (timeOutURL.length > 0) {
                resetUrl = timeOutURL.replace(/expire$/, "resettimeout");
                if (totalTimeoutMilliseconds !== null) {
                    root.clearTimeout(timeoutWarningID);
                    root.clearTimeout(timeoutID);
                    
                    timeoutIntervalId =
                            root.setInterval(resetTimeout /* defined below */,
                                    root.warningTimeoutMilliseconds);
                }
            }
        }
    }, 30000);

    var resetTimeout = function () {
        xhr("GET", resetUrl)
                .success(function (msg) {
                    /* do nothing */
                })
                .error(function (xhr, errMsg, exception) {
                    alert("failed to reset timeout");
                    /* error; fallback to delivered method */
                    (root.setupTimeout || root.setTimeout2)();
                });
    };
}(window));

A special "shout out" to Todd Motto for his Standalone Ajax/XHR JavaScript micro-library which is embedded (albeit modified) in the JavaScript above.

Wednesday, July 16, 2014

PeopleCode Coding Discipline

Java, JavaScript, C++, C Sharp, Objective C, Groovy... what do these languages have in common? Yes, curly braces, but besides that... actually, there are a lot of similarities between these languages. Throw Visual Basic, Perl, Python, or any other well-known language into the mix and the number of similarities drops significantly. Setting semantics and syntax aside, a common attribute of all well-known languages is standards and best practices. Some of those best practices (such as coding style) differ by language. For example, bash scripts can either look like, uh... bash scripts or they can look like c-style logic statements. Obviously, bash best practices prefer you make bash code look like bash code. Other standards are personal: do you prefer real tabs or spaces? How many spaces does your tab consume? Do you put curly braces on a new line?

How does all of this fit into PeopleCode? Forget about code formatting preferences. Application Designer has its own code formatting ideas. But there are other best practices that can help you write better code with fewer defects (fewer defects = better code). By following best practices your code will be easier to read, you will be more productive, and your users will be happier because you deliver better solutions faster.

Even though best practices usually result in code that is more efficient to process, that isn't really the point. Computers can interpret just about anything. Compilers and interpreters are really good at eliminating useless words and resolving seemingly incomprehensible logic. I love Martin Fowler's quote, "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." Best practices are really about writing code that humans can easily comprehend. For example, avoid complex logic (including double negatives, or any negative logic, for that matter), keep your method and function code short, etc. If you write some code, leave it for a night, and then come back the next day and either need to read lots of comments to figure it out or spend a few minutes "remembering" what that code does, then the code is probably too complex. The problem with complex code is that it is easily misinterpreted by humans. Another problem with complex code is we actually ignore it when trying to resolve problems. We know it takes time to digest complex code, so we avoid it, preferring to test simple code first. Why waste time trying to understand complex code if it might be functioning properly?

Today's Quest Newsletter contained a link to 10 Bad Coding Practices That Wreck Software Development Projects. These are language agnostic practices that we can easily apply to PeopleSoft development.

If I were to summarize Coding best practices, I think I would do it like this: two.sentenc.es. Now, arguably, short does not equal comprehensible. There are programmers that err on the terse side because it is clever. This is true, often short code is clever. It is also hard to read. Most of us, however, err the other way. E. F. Schumacher said, "Any fool can make things bigger, more complex, and more violent. It takes a touch of genius — and a lot of courage — to move in the opposite direction." Schumacher died in 1977, so this problem is not new.

Computer programming is about communication. As programmers we have two audiences:

  • Computers (which can interpret anything -- even complex stuff)
  • Humans (who have a limited attention span, distractions, and a preference for simplicity)

Here is why I think discipline and best practices are critical for good PeopleCode:

We use PeopleCode to create business rules, but PeopleCode is NOT a business rules language. PeopleCode is a Metadata manipulation language. (Note: this is purely my opinion)

Here is why I believe PeopleCode is for metadata, not business rules: PeopleCode only has Metadata objects: records, fields, SQL, components, menus, etc. These are all metadata. These are the low level API's we use to write business logic. Consider the following PeopleCode:

Local record &rec = CreateRecord(Record.PSOPRDEFN);
Local field &descr;

&rec.SelectByKey("jimsoprid");
&descr = &rec.GetField(Field.OPRDEFNDESC);

&descr.Value = "Jim Marion";

&rec.Update();

This code implements business logic, but does so by manipulating metadata objects. PeopleCode metadata objects are building blocks for business logic. If we were to rewrite this using a business logic language, it would probably look something like this:

Local User &u = GetUser("jimsoprid");

&u.descr = "Jim Marion";
&u.Update();

And this is why discipline and best practices are SO important for PeopleCode developers: We are trying to speak business logic with a metadata vocabulary. We start with a communication deficit. It is like trying to teach advanced weaving using an automobile mechanics vocabulary. The two subjects have different vocabularies. But if you combine the words correctly, you can communicate the same meaning.

Monday, July 14, 2014

PeopleTools 8.54 is GA!

On Friday the PeopleTools blog announced that PeopleTools 8.54 is now Generally Available (GA). PeopleTools 8.54 brings several usability features including responsive design for mobile devices as well as development features such as Mobile Application Platform (MAP). One of my favorite new features is component specific branding. You can now attach stylesheets and JavaScript to components as described in PeopleBooks Applying Branding to Other Objects. Another great branding enhancement is Attribute-based branding. This is very similar to role based branding, but more flexible and easier to administer. You can read about it in the PeopleBooks entry Administering System Branding.

To learn more read the PeopleTools announcement or visit the hosted 8.54 PeopleBooks. I can't wait for the new demo images!

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 http://www.questdirect.org/learn/2629061/.

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, 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;
      End-If;
   End-If;
   
   %Response.RedirectURL(&url);
End-Function;

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);
   
private
   
   instance JavaObject &printlnMethod_;
   instance JavaObject &outputStream_;
end-class;

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);
   End-If;
   
   &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();
end-method;

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

Tuesday, August 27, 2013

PeopleSoft Interaction Hub VM Demo Image Available

The PeopleTools team just posted information about a new VirtualBox image for PeopleSoft Interaction Hub. Read more about it on the PeopleSoft Technology Blog.

Tuesday, July 23, 2013

Greffins Feather on PSUnit

I really enjoy hearing about developers incorporating proven methodologies into their PeopleSoft development practices. One of my favorite's is Test Driven Development. Lee Greffin has grabbed onto this concept and is sharing his experiences on the Greffins Feather blog. You can read his PSUnit series here.

Monday, July 22, 2013

OpenWorld 2013

OpenWorld 2013 is about two months away. Those of you that are attending probably noticed that the content catalog is posted. Here are the sessions I will be delivering:

A few other sessions I am really looking forward to attending are

Wednesday, June 26, 2013

Using PeopleCode Modals: openPageletActionPageInModal (PeopleTools 8.53)

While typing something in the Chrome JavaScript console, I managed to bring up the function openPageletActionPageInModal. Hmmm, sounds interesting. I drilled to the implementation and saw that you call it like this:

openPageletActionPageInModal("http://your.server.name/psp/ps/EMPLOYEE/HRMS/c/ROLE_EMPLOYEE.HR_EE_PERS_INFO.GBL?")

What does it do? It converts the /psp/ part of the URL to /psc/ and opens the URL in a modal dialog. For me it worked great as long as I followed two simple rules:

  • Included a question mark in the URL. If the URL has no query string parameters, just include a trailing "?".
  • Used this function in the same datababase/application as the content. In other words, I didn't try to use this technique in Interaction Hub for HRMS content. For that to work, I would need to translate more than just /psp/. I would also have to translate the server name and site name.

Note: It works just fine in a portal/content provider situation if the URL is already a /psc/ URL.

Anyway, interesting, undocumented piece of JavaScript that you may or may not find useful. As always, your mileage may vary.

Note: I was using PeopleTools 8.53 when I found this function.

Tuesday, June 25, 2013

Using the PeopleTools 8.53 Version of jQuery "Safely"

Now that PeopleTools 8.53 includes jQuery, the question is, "how do I use it?" Probably the easiest way is to add something like this to your Pagelet or page where you want to use jQuery (see New PeopleTools 8.53 Branding Tools):

<script type="text/javascript" src="/psc/ps/EMPLOYEE/EMPL/s/WEBLIB_PTBR.ISCRIPT1.FieldFormula.IScript_GET_JS?ID=PT_JQUERY_1_6_2_JS"></script>

Some of you may have tried this. It works great if you have just one item that uses jQuery. If you add more (for example, if you add multiple pagelets) where some use plain jQuery, some use jQuery UI, and others use jQuery Cycle, you will see that each new inclusion overwrites your plugin list, rendering your new pagelet unusable. What about include protection? This concept is still relevant, but I would hate to manage it as a modification if there was another way. Here is what I came up with for 8.53:

<script type="text/javascript" src="/psc/ps/EMPLOYEE/EMPL/s/WEBLIB_PTBR.ISCRIPT1.FieldFormula.IScript_GET_JS?ID=PT_JQUERY_1_6_2_JS"></script>
<script type="text/javascript">
  // special variable to store "our" single jQuery instance
  if(!window.psjq$) {
    window.psjq$ = window.$ = window.jQuery;
  } else {
    // ignore recent import and use the global single instance
    window.jQuery = window.$ = psjq$;
  }
</script>

If you look at the PT_JQUERY_1_6_2_JS HTML definition in app designer, you will see that PeopleTools uses jQuery.noConflict() to free up $, but not jQuery. Using noConflict to move $ into a new variable named ptjq162 is appropriate, but doesn't help with plugins. Properly coded plugins use the global variable window.jQuery. Looking through jQuery UI, cycle, and many of the other plugins I use, they all use the global jQuery variable, with no mechanism for replacing it. As you can see here, my solution is to store jQuery in the custom global variable psjq$, and then override jQuery on each import of jQuery. This way all plugin scripts loaded after my script will always use the original, single instance jQuery copy of psjq$. Note: I tried using ptjq162.noConflict(true) to manage a single instance, but didn't get it working. My approach just seemed easier for me to understand, and, well, it just worked.

I don't have much "burn in" time with this, so I'm open to suggestions. One key difference between this approach and the Include Protection approach is this approach processes jQuery for each jQuery include. It drops it after processing it, but you still take the performance hit (you may not notice it, but it is still there). The include Protection approach only processes jQuery once.

Note: My script above assumes $ is for jQuery. The point of jQuery's noConflict is compatibility with other libraries that use $. If you find yourself in this situation, just remove $ from the assignments above. For compatibility reasons, you should probably never use $ except in a closure, but...

Warning: PT_JQUERY_1_6_2_JS is NOT safe for use with multiple versions of jQuery. The noConflict call at the end of the script is compatible with $, so if you have another, more recent version of jQuery, $ will still point to your newer version. The jQuery variable, however, will only point to the last version parsed (or the last assignment, as shown above). $ is nice, but it is really the jQuery variable that matters. To be compatible with other versions of jQuery, the HTML definition would have to use jQuery.noConflict(true). The jQuery docs don't recommend having two versions on the same page, but it is important to note.

Friday, June 21, 2013

Pagelet Wizard Custom Tags

Pagelet Wizard custom transformations can use special tags documented here to insert images, message catalog entries, or to format numbers and dates. This is great when trying to format currencies or ensure multilingual compliance. The problem with "Post-Transformation Processing," as it is called in PeopleBooks, is that it requires the transformation results to be valid XML. Question: How do you get Pagelet Wizard to generate valid XML when the Xalan processor used by PeopleTools sees HTML tags and automatically generates HTML? Answer: use the <xsl:output> XSL tag. Here is a sample template that produces valid XML:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xhtml="http://www.w3.org/1999/xhtml"
    xmlns="http://www.w3.org/1999/xhtml" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    exclude-result-prefixes="xhtml xsl">

    <xsl:output method="xml" version="1.0" encoding="UTF-8"
        doctype-public="-//W3C//DTD XHTML 1.1//EN" doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
        indent="yes" />

    <xsl:template match="/">
        <!-- your XSL goes here -->
    </xsl:template>
    
  <!-- identity transform templates -->
  <xsl:template match="*">
    <xsl:apply-templates/>
  </xsl:template>

  <!-- delete unmatched text -->
  <xsl:template match="@*|text()|comment()|processing-instruction()">
  </xsl:template>
  
</xsl:stylesheet>

New PeopleTools 8.53 Branding Tools

If you have an instance of PeopleTools 8.53, you may have noticed a new component at PeopleTools > Portal > Branding > Branding Objects. This new component allows you to upload images, HTML definitions, JavaScript definitions, and Stylesheet (CSS) definitions. The uploaded definitions become managed definitions in Application Designer. The point is to make it possible for customers to create and maintain user experience definitions online rather than having to log into App Designer to create and maintain these definitions. Once uploaded, if you prefer, you can still view and maintain these same definitions with Application Designer (but it is recommended that you maintain them using the Branding Objects component instead of App Designer). Note: the image upload does not yet support PNG (can you imagine a beautiful web without PNG? Me neither). I hope to see PNG in a future release. You can still create PNG's in App Designer, just not through the new Branding Objects component.

Being able to upload and create App Designer images, JavaScripts, and CSS files through an online component is nice, but where can you use these definitions? Just about anywhere that you see an image prompt. Here are some examples:

  • Navigation Collections
  • Pagelet icons
  • Pagelet Wizard HTML and XSL

Navigation Collections are pretty self explanatory. You use the prompt to select an image. Pagelet Wizard, on the other hand, is quite open. One way you can use these images is with Pagelet Wizard's custom XSL PSIMG tag:

<PSIMG ID="MY_UPLOADED_IMAGE" BORDER="0" />

Another way to use these definitions (and any other JavaScript, CSS, or image definition in App Designer) with Pagelet Wizard is through a collection of new iScripts (with examples):

  • IScript_GET_JS: http://your.peoplesoft.server/psc/ps/EMPLOYEE/EMPL/s/WEBLIB_PTBR.ISCRIPT1.FieldFormula.IScript_GET_JS?ID=PT_JQUERY_1_6_2_JS
  • IScript_GET_CSS: http://your.peoplesoft.server/psc/ps/EMPLOYEE/EMPL/s/WEBLIB_PTBR.ISCRIPT1.FieldFormula.IScript_GET_CSS?ID=PSJQUERY_BASE_1_8_17
  • IScript_GET_IMAGE: http://your.peoplesoft.server/psc/ps/EMPLOYEE/EMPL/s/WEBLIB_PTBR.ISCRIPT1.FieldFormula.IScript_GET_IMAGE?ID=PT_LOCK_ICN

I avoid hard coding server names in URL's if at all possible. To avoid hard coding the server name, start your URL with /psc/, skipping the server portion. When using this relative approach, though, keep in mind the pagelet's runtime context. If this is a local pagelet in a content provider or Interaction Hub, a relative URL will work just fine. However, if the pagelet is a remote pagelet, coming from a content provider, this relative approach will not work. Another thing to keep in mind when using these iScripts is your instances site name. Even with a relative URL, you still have to hard code your instance's site name, which usually differs between development, test, QA, and production instances.

Friday, June 07, 2013

HIUG Interact 2013

HIUG starts on Sunday. Great weather, beautiful setting... expect an excellent conference! I hope you will be ready to start the conference early on Sunday :). At 2 PM, Brent Mohl and I will deliver a Hands on workshop which gives attendees an opportunity to build content with new PeopleTools 8.53 User Experience tools. Here is a list of sessions I and my colleagues are involved with:

  • Sunday 2 PM to 4 PM -- 13082:Hands-on Workshop-Delivering a Ground-Breaking User Interface with PeopleTools & the Interaction Hub (Lowell B)
  • Monday 12:45 PM to 1:45 PM -- 13141:PeopleTools 8.53 in Action (Kirkland)
  • Tuesday 10:30 AM to 11:30 AM -- 13084:Understanding PSFT Maintenance Tools & How They Fit Together (Trailblazer B)
  • Tuesday 4:45 PM to 5:30 PM -- 13085:PeopleTools Product Round Table Discussion with Oracle Representatives (Trailblazer B)
  • Wednesday 9:30 AM to 10:30 AM -- 13083:Delivering a Ground-Breaking User Interface Using PeopleTools and the Interaction Hub (Trailblazer B)

I'm looking forward to hearing Cincinnati Children’s Hospital Medical Center talk about TDD and PSUnit (Jun 11, 2013 02:15 PM - 03:15 PM in Trailblazer B). Also, be sure to attend the "Have Your PeopleSoft Systems Been Hacked?" session led by Larry Grey Jun 10, 2013 (12:45 PM - 01:45 PM). I attended this one at Alliance. Great session! ... of course, it is offered at the same time as my PeopleTools 8.53 in Action session :(

Friday, May 31, 2013

jQuery in PeopleTools 8.53

A lot of customers are now working with PeopleTools 8.53. If you open Application Designer and search for HTML definitions named PT_JQUERY, you will see there are 2 (sometimes more) new jQuery JavaScript definitions included with PeopleTools 8.53:

  • jQuery 1.6.2
  • jQuery UI 1.8.17
  • Mobile jQuery (modules with mobile apps)

You no longer need to download, upload, or otherwise install jQuery to use it with PeopleSoft applications. One interesting thing I noted in the PeopleTools jQuery file is that the end of the jQuery file uses jQuery.noConflict() to replace $ with ptjq162. Unfortunately, it doesn't take advantage of the include protection I described in my post jQuery Plugin Include Protection, so be careful using it in pagelets directly.