Wednesday, October 05, 2011

Changing the Search Page Operator

I just posted about Monkey Patching, a technique used in chapter 7 of my book PeopleTools Tips and Techniques to set the default search page operator on advanced search pages (Note: only 8.50 and later required Monkey Patching). As I was looking over the "Changing Search Operators" section of chapter 7, I noticed the code was missing a few lines (pages 293 - 296). Here is my revision:

<script type="text/javascript">
  // C style include protection
  if(!window.apt.setSearchOp) {
    window.apt.setSearchOp = true;
    
    if(window.net) {
      // pt 8.50
      (function($) {
        var originalContentLoader = net.ContentLoader;
        net.ContentLoader = function(url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt) {
          var originalOnLoad = onload;
          if(name == "#ICAdvSearch") {
            onload = function() {
              if (typeof originalOnLoad == "undefined" || !originalOnLoad) {
                this.processXML();
              } else {
                originalOnLoad.call(this);
              }
      
              // The value for "between" is 9. Change this to your desired
              // search operator value.
              var newValue = 9;

              // The name of the search key field is APT_UI_SCRIPTS_MENUNAME.
              // Generally speaking, PeopleSoft creates HTML element names by
              // combining record and field names with an underscore as in
              // RECORD_FIELD. Change the following value to the name of your
              // search key record_field
              var coll = $("select[name='APT_UI_SCRIPTS_MENUNAME$op']");
              if(coll.val() != newValue) {
                coll.val(newValue).change();
              }
            }
          }
          return new originalContentLoader (url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt);
        }
      })(window.jQuery);
    } else {
      // pt 8.4x, $(document).ready below will handle pt 8.4x
    }
  
    // just in case advanced is the initial view
    $(document).ready(function() {
      var newValue = 9;
      var coll = $("select[name='APT_UI_SCRIPTS_MENUNAME$op']");
      if(coll.val() != newValue) {
        coll.val(newValue).change();
      }
    });
  }
</script>

Tuesday, October 04, 2011

Monkey Patching PeopleSoft

As a PeopleSoft developer responsible for upgrades and maintenance, I work extra hard up front to avoid changing delivered code. My potential reward is less work at patch, bundle, or upgrade time. One way I deliver new user interface features without modifying delivered code is by writing Monkey Patches. Monkey Patching is a term used with dynamic languages for modifying runtime behavior without changing design time code. Dynamic languages, such as JavaScript support this by allowing developers to override, extend, or even redefine objects and methods at runtime. Let me set up a scenario:

In PeopleTools 8.49 and earlier, I could tell when an action happened in a component (FieldChange, Save, Prompt, etc) by listening for the window load and unload and document ready events. PeopleTools 8.50, however, triggers these events through Ajax requests, which means the page state doesn't change. With 8.50, I had to find an alternative JavaScript mechanism for identifying these same actions, and the PeopleTools net.ContentLoader JavaScript object seemed just the ticket. By wrapping this JavaScript object with my own implementation, I can hook into the PeopleTools Ajax request/response processing cycle. If you have Firebug and PeopleTools 8.50 (or higher), then load up your User Profile component (/psc/ URL only) and run this JavaScript:

(function() {
  var originalContentLoader = net.ContentLoader;
  net.ContentLoader = function(url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt) {
    console.log(name);
    return new originalContentLoader (url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt);
  }
})();

Next, click on one of the prompt buttons on the user profile General tab. You should see the name of the button you clicked appear in the Firebug console. Notice that the button name appears in the Firebug console before the Ajax HTTP Post. If you wanted to take action after the Ajax response, then you would implement your own onload handler like this:

(function() {
  var originalContentLoader = net.ContentLoader;
  net.ContentLoader = function(url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt) {
    console.log(name);
    
    var originalOnLoad = onload;
    onload = function() {
      if (typeof originalOnLoad == "undefined" || !originalOnLoad) {
        this.processXML();
      } else {
        originalOnLoad.call(this);
      }
      console.log("Ajax response received");
    }

    return new originalContentLoader (url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt);
  }
})();

Notice that the text "Ajax response received" appears after the HTTP post, meaning it executed after the page received the Ajax response.

When creating Monkey Patches, it is critical that you consider the original purpose of the overridden code. In this example we redefined the net.ContentLoader, but maintained a pointer to the prior definition. It is possible that another developer may come after me and create another patch on net.ContentLoader. By maintaining a pointer to the net.ContentLoader, as it was defined when my code ran, I ensure that each patch continues to function. In essence, I'm developing a chain of patches.

Monkey Patching has a somewhat less than desirable reputation, and for good reason. If allowed to grow, patches on patches can make a system very difficult to troubleshoot and maintain. Furthermore, if one patch is not aware of another patch, then it is entirely possible that a patch could be inserted in the wrong place in the execution chain, upsetting the desired order of patches.

"With great power comes great responsibility" (Voltaire, Thomas Francis Gilroy, Spiderman's Uncle Ben? Hard to say who deserves credit for this phrase). Use this Monkey Patching technique sparingly, and be careful.

Monday, October 03, 2011

Accordion Navigation Collections

A few months ago we released a White paper about PeopleSoft Applications Portal and WorkCenter Pages that showed screen shots of an accordion menu. A lot of you asked how we created these pagelets. Tomorrow in our OOW session PeopleSoft Answers: How to Create a Great PeopleSoft UI, I will demonstrate creating the pagelet, but we won't have time to walk through the XSL -- the critical piece. For those of you that will be there (and those that won't but know how to use Pagelet Wizard), here is the XSL: accordion-nav-hosted.xsl.

Disclaimer: I make no warranty regarding the use of this XSL.

Security Warning: To make sure the XSL will work "out of the box," I pointed the JavaScript at Google's hosted JavaScript API's. Since this code is used on your enterprise home pages, I suggest you replace these references with references to your own site's versions of these libraries. The thought of allowing some external service to run code on my pages makes me a bit nervous.

I have to point out a minor difference between the output of this XSL and the output shown in the white paper: This XSL opens links in the current window or a new window. It does not use modal dialogs. Navigation Collection XML contains absolute PSP URL's, which don't display well in a modal dialog. The version shown in the White paper actually uses a custom transformer and some PeopleCode to convert psp URL's into psc URL's for dialogs.

Update March 5, 2012: Leandro, a reader of this blog, posted his derivative of this stylesheet. You can download it here. Leandro wants to make sure you know that it works on the single nav collection for which he tested, but other exceptions may arise. I looked through the XSL, and it looks good. Here is a list of the differences between Leandro's version and mine:

  • Updated the links to jQuery and jQuery UI (JS and CSS) to the latest versions.
  • Commented out the custom dialog framework code that would open jQuery UI dialog IFrames (because the code to make the iframe is not present in Jim's original).
  • Included the description of the top-level folders in the H3 tag's title attribute, so mouse-over of the accordion items will display the description of the menu. (This was already done by you for inner folders and shortcuts.)
  • The resulting accordion menu will be sorted as you would typically find in a PeopleSoft navigation collection: all folders before all shortcuts, and the "# more..." pseudofolder (if any) at the end.

Thursday, September 22, 2011

Creating Binary Arrays

Lately I have been using PeopleCode to manipulate binary files: moving files, copying files, and even creating zip files. A prerequisite for reading from and writing to binary files is the basic binary array -- the buffer. My blog post Base64 Encoding for PeopleSoft demonstrated a very complicated method for creating binary files that worked with PeopleTools 8.49 and earlier, but does not work on my PeopleTools 8.51 systems. While studying PeopleBooks I found a much easier, well documented method for creating binary arrays:

Local JavaObject &bytes = CreateJavaArray("byte[]", 1024 /* length of array */);

For arrays with known values at construction time, you can use the CreateJavaObject function:

Local JavaObject &bytes = CreateJavaObject("byte[]", 5, 10, 15, 20);

Note: Since this is documented, I suspect these functions will work with PeopleTools 8.49 and earlier, but I haven't tested them on earlier PeopleTools versions. If this method won't work in PeopleTools 8.49 or earlier, then you are welcome to use the alternative:

REM ** get a reference to a Java class instance for the primitive byte array;
Local JavaObject &arrayClass = GetJavaClass("java.lang.reflect.Array");
Local JavaObject &bytes = &arrayClass.newInstance(GetJavaClass("java.lang.Byte").TYPE, 5);

I would like to call out Kris who posted a comment on Base64 Encoding for PeopleSoft stating that the older method no longer worked. I happened to be working on zipping files with PeopleCode the week before Kris's comment and discovered the same issue and resolution. Very timely.

Wednesday, September 21, 2011

OpenWorld Schedule 2011

I can hardly believe it. One more week at home and then I'm off to San Francisco for the biggest Oracle show of the year. I will be in San Francisco pretty much all week and would love to meet any of you that are attending. Here is where you will find me:
  • Monday 9:45 AM to 1:30 PM -- Integration Broker demo pod
  • Tuesday 9:45 AM to 12:00 PM -- Integration Broker demo pod
  • Tuesday 1:15 PM to 2:15 PM -- Session 14020 PeopleSoft Answers: How to Create a Great PeopleSoft UI (Moscone West 2024)
  • Tuesday 3:30 PM to 4:30 PM -- Session 14003 PeopleSoft PeopleTools Tips and Techniques (Moscone West 2022)
  • Wednesday 10:00 AM to 10:30 AM -- Meet the Authors @ the Moscone West bookstore (bring your book so I can sign it)
  • Wednesday 12:30 PM to 4:00 PM -- Integration Broker demo pod
  • Thursday 10:00 AM to 10:30 AM -- Meet the Authors @ the Moscone West bookstore (bring your book so I can sign it)

Wednesday, April 06, 2011

Collaborate 2011 Schedule and Book Signing

Collaborate is only a couple of days away. If you are a reader, I'd love to meet you. Here are some times and places we can connect:

  • Monday, 5/11 6:00 PM - 8:00 PM -- Exhibition Hall PeopleTools demo pod.
  • Tuesday, 5/12 10:15 AM - 2:00 PM -- Exhibition Hall PeopleTools demo pod.
  • Tuesday, 5/12 3:15 PM - 4:15 PM -- Session 85680 PeopleTools 8.51 Highlights - PeopleTools in Action room 203B (Quest)
  • Tuesday, 5/12 4:30 PM - 5:00 PM -- Book signing at the Collaborate book store
  • Tuesday, 5/12 5:30 PM - 7:00 PM -- Exhibition Hall PeopleTools demo pod
  • Wednesday, 5/13 8:00 AM - 9:00 AM -- Session 85670 PeopleTools Tips and Techniques room 203A (Quest)
  • Wednesday, 5/13 10:15 AM - 4:00 PM -- Exhibition Hall PeopleTools demo pod

McGraw Hill was also able to schedule a special book signing event for Tuesday between 4:30 PM and 5:00 PM. Follow me down to the book store after my PeopleTools in Action session, buy a book, and I'll sign it for you. If I lose you, I've been told the bookstore is next to registration. See you there!

Tuesday, April 05, 2011

Pagelets, WorkCenters, etc in White Paper

Some of my friends from sales and product strategy just put together a very nice white paper on some of the new features in PeopleTools and Applications Portal (formerly known as Enterprise Portal). I created some of the content in the screen shots (home page layout, accordion navigation collections, slide show news publications, etc) and they did all the writing. Download a copy and see what you can do with your PeopleSoft application. The paper is titled PeopleSoft Applications Portal and WorkCenter Pages.

Monday, April 04, 2011

blogger.com's Views

Have you seen blogger.com's new dynamic views? For my site, most aren't that exciting, but the flip card view for labels is pretty interesting. Rather than just seeing the usual label (count), you can see the actual titles under each label.

Wednesday, February 16, 2011

Alliance 2011

I'm just finishing my demos for this year's Alliance conference. I will present session 29321, PeopleTools Tips and Tricks, on Monday morning from 9:30 to 10:30 in Korbel 4A-C. In this session I will present some mobile, mashup, and ajax solutions as well as tips for debugging these client/server HTTP interactions. On Tuesday, I will show you some of the new PT 8.50/51 features during the session 29332 "PeopleTools 8.51 in Action" at 12:45 in Korbel 4A-C.

If you are not able to attend these sessions, then please feel free to chat with me in the Exhibition Hall or at Meet the Experts. I will be in the Exhibition Hall Sunday from 5:30 to 8:30, Monday from 12:45 to 2:15, and again on Tuesday from 12:30 to 2:15. I will be at the Meet the Experts PeopleTools table on Monday from 4:30 to 5:30 and on Tuesday from 9:30 to 10:30.

I can't wait to see you there!!

Thursday, February 10, 2011

JavaScript Meta blog

JavaScript is a critical component of PeopleSoft applications. With the web browser pretty much taking over as the rendering engine for enterprise applications, I see JavaScript as a critical language for computer professionals. Of course, technologies like PeopleTools and ADF contain abstraction layers so application developers do not have to write JavaScript, but, in the end, someone has to write the JavaScript generated by those abstraction layers. And, if something goes wrong, odds are very good you will have to dig through the generated JavaScript to see what went wrong (where, when, why, etc).

Anyway, I think JavaScript is one of the most important modern languages a programmer can learn, and I know I'm not alone in this opinion. Of late, I've found some very interesting online resources for people interested in learning JavaScript, so I wrote this post to pass those resources along to PeopleSoft developers.

I will maintain this as a "Meta-blog" post and update it as I find more resources. I will attempt to keep the list short, so you don't have to sift through thousands of irrelevant tutorials. Restated: this is not a complete list. It is just a short list of tutorials that I think stand above the rest.

Wednesday, September 29, 2010

OOW 2010 Presentations Available

Oracle OpenWorld OnDemand is now available and includes slides and audio for each of the sessions in which I presented. Here are the OnDemand links:

If you were not able to attend OpenWorld 2010, you can purchase OnDemand here.

Thursday, September 23, 2010

Going Mobile with PeopleSoft

Chapter 14 of my PeopleTools Tips & Techniques book walks you step-by-step through creating a mobile application. That chapter uses a CI based web service and Oracle ADF to demonstrate some of the simple drag-and-drop tools provided by Oracle. Even though the technique demonstrated appears simple, if you start to dig into the generated code and try to work directly with the Web Service Data Control, you will quickly see that JDeveloper does a very, very good job of hiding the real complexities behind web services. For this year's OpenWorld, I wanted to show just how simple it could be to create a mobile app for PeopleSoft. For my prototype, I chose to build a mobile worklist out of plain HTML, JavaScript, and CSS (it seems to me that plain HTML, JavaScript, and CSS is about as simple as web development gets). Without a server side technology like JDeveloper's ADF and JSF, I knew my mobile app would have to communicate with PeopleSoft using Ajax. As it turns out, most modern mobile browsers support XHR (as of BlackBerry 6, Torch, the BlackBerry browser is now WebKit - YEAH!!!), but I knew having a good mobile JavaScript library like jQuery would certainly help. A quick google search turned up xuijs, which happens to be modeled after jQuery. Using jEdit, my favorite syntax highlighting text editor, I prototyped the user interface, substituting Ajax URL's for local text files. After ironing out the server side requirements, I set about creating the Integration Broker App Class synchronous request handlers that my app would require. To make my HTML and JavaScript as simple as possible, I wrote my handlers to return data in JSON and JSONP format. While my JavaScript and PeopleCode may prove to be of some interest to you, I believe the most important concept from this exercise is the mechanism for calling Integration Broker from Ajax. To execute a web service from an HTTP GET (basic Ajax in REST-like fashion), you use a URL similar to:

http://your.peoplesoft.server/PSIGW/HttpListeningConnector?Operation=YOUR_OPERATION_NAME.v1&OperationType=Sync

Calling any service operation implies, of course, that you have a message, service, service operation, handler, and an any-to-local routing.

My point for sharing this is that we easily forget how simple an application can be. PeopleTools provides the integration architecture. It is up to us to pick a language we are comfortable developing with. If your organization prefers .Net over Java, then write your web based mobile app in .Net. The language doesn't matter. Pretty much any language can make an HTTP request to the Integration Broker and then process the response. The keys are:

  • Knowing how to call Integration Broker
  • Remembering that the mobile device has a much smaller screen

That is about all there is to building mobile applications. Pretty simple... right?

Posting Data to IScripts

If you are a regular reader, you already know that I am a big fan of Ajax. Most of my PeopleSoft Ajax requests use HTTP GET operations to send query string parameters to iScripts. I have considered using POST to send structured data to iScripts (XML, JSON, etc), but have not found reason to do so. Considering my background in other web based languages, I just assumed the %Request object provided direct access to posted content. I didn't really look until I saw an IT Toolbox forum question from KCWeaver asking how to post data to an iScript. The Request object does have a GetContentBody() method that will return POST'd data. What PeopleBooks doesn't tell you is how to activate the GetContentBody method (Note: I don't think this is an oversight. I think it is because GetContentBody is designed for Business Interlinks, not for iScripts). Special thanks to Kevin for digging through the documentation and figuring out how to POST to an iScript. The trick is to add postDataBin=y to the end of your query string.

View the full IT Toolbox thread here: AJAX to iScript

PeopleTools Tips Sample Chapter Available

Are you still trying to decide whether or not to buy my new PeopleTools book? Would a sample chapter help? The McGraw Hill page for this book allows you to download chapter 3 for free. Chapter 3 contains step by step instructions for workflow enabling a transaction using the relatively new Approval Workflow Engine (new in PeopleTools 8.48). If you have ever had trouble configuring AWE and wondered if it was possible to trace the stage, step, path, approver selection information, you will want to take a look at the Tracing AWE sidebar on page 125 (page 35 of the PDF).

Saturday, September 18, 2010

PeopleTools Tips at OpenWorld

I have checked in at my local airport and am en route to OpenWorld, the largest tech conference of the year. I'm pretty well finished preparing my demos for this year's session, and am using this time to finish my slides. I am very pleased with my demos this year as I think they demonstrate some very powerful ways to enhance PeopleSoft applications. Two of my primary topics for this year are Mashups and Mobile. I see Mashups as an alternative to Integration. Of course, you still need integrations, but whenever possible, I look for a Mashup alternative because Mashups general don't require modifications. In this session I will present some Mashup ideas and ways to ensure security.

Mobile... I find mobile to be one of the most fascinating ideas. I work remote (no office), and, therefore, am 100% mobile (at least in theory). PeopleTools has been relatively silent in regards to mobile. PeopleSoft Applications have built some very exciting mobile apps (see Theresa's blog post and video), but PeopleTools is silent. After reviewing a handful of mobile development strategies, I am actually quite pleased with the mobile development solutions available to PeopleSoft customers. I'm finding that even though PeopleTools is silent in regards to mobile, the PeopleTools architecture lends itself very well to mobile development. In my PeopleTools Tips session on Monday I will demonstrate two separate mobile applications. The first is a mobile employee directory built using ADF and the web service data control. The second application uses plain old JavaScript and HTML to display a mobile worklist. This second application excites me the most because it shows that mobile development can be simple - No SOAP, no WSDL, no frameworks, no data bindings... just plain JavaScript, CSS, HTML, and PeopleCode.

Besides mobile and mashups, I also included:

  • Monkeypatching - what is it and how can I/why would I use it?
  • Debugging integrations - tools that facilitate debugging.
  • Pagelet Wizard - what is it, how can I use it, how can I extend it?

See you Monday at 5:00 PM in the Marriott, Golden Gate A (session id S317016). You won't be disappointed!

Friday, September 10, 2010

PeopleTools 8.51 Now Generally Available

PeopleTools 8.51 is now Generally Available (meaning, you can download it from eDelivery). You can find the hosted PeopleBooks for 8.51 here. The PeopleTools 8.51 documentation home is here.

Tuesday, September 07, 2010

URL Administration (Nodes and URL Definitions)

I use URL's quite extensively for Ajax and other non-Integration Broker integrations (NEVER HARD CODE URL'S!). PeopleTools provides a couple of ways to store URL information. The most well-known of these features is the URL definition (PeopleTools > Utilities > Administration > URLs). URL Definitions are great for relative URL's, but I'm not fond of storing fully qualified URL's in this manner. Here is why...

Imagine having 5 URL's that point to different resources on the same server and then one day the server's host name changes. Because of this change I have to modify 5 URL definitions. What if I forget one?

An alternative is to store the base URL in a node definition and then the relative portion of the URL in a URL definition. Creating a fully qualified URL in this manner requires concatenating two definitions: the node URI and the URL definition. Next Question: How do I access these Meta-data objects from PeopleCode? Most of us are familiar with the GetURL PeopleCode function, but what mechanism does PeopleCode offer for retrieving a node's Content and Portal URI?

A PeopleSoft instance's node definitions are accessible through the %Session object. The Session object contains a method named GetNodes which returns a collection of the instance's node definitions. A call to the collections FindItemByName method returns a reference to a single node, which, of course, has properties of its own. Putting this all together, returning the Portal URI of a node named UCM would require PeopleCode that looks something like:

Local string &serverUrl = %Session.GetNodes().ItemByName("UCM").PortalURI;

By centralizing the base portion of the URL in a node definition, we save some administration overhead.

Tuesday, August 31, 2010

OpenWorld in Two Weeks!

OpenWorld is almost here! In less than 3 weeks, we will all be together again for the biggest Oracle apps and technology reunion of the year... and possibly, the biggest ever, with JavaOne and Oracle Develop co-located with OpenWorld.

This year I am teaming up with my good friend Graham Smith to deliver the "best of" PeopleTools Tips for 2010. Expect to see more PeopleTools 8.50 content in our presentation this year. Graham and I will be on stage Monday evening from 5:00 PM to 6:00 PM at the Marriott Golden Gate A (session id S317016). You will NOT want to miss this session!

On Thursday you can see Matthew, Pramod, and myself present Monster Mashups, a session about creating mashups using the PeopleTools 8.50 related content framework. That session will be held at Moscone West room 2014 from 12:00 PM to 1:00 PM (session ID S317448).

Besides these sessions, I'll be working the PeopleTools Integration Tools demo pod Monday morning and all of Tuesday. Later during the week, however, I hope to spend some time in the Fusion Apps UI demo pod.

On Wednesday I plan to spend a half hour at the Oracle Bookstore signing copies of my new book. I will be there from 10:00 AM to 10:30 AM during the Meet the Authors time slot. If you have a copy of my book, bring it with you so I can sign it! If you don't have a copy, I'm sure the Oracle bookstore will be more than happy to sell you a copy. Meet the Authors actually runs Monday, Tuesday, and Wednesday from 10:00 AM to 10:30 AM, but I have demo grounds responsibilities Monday and Tuesday, so I won't be able to attend the first two days.

Friday, August 20, 2010

Get Your Kindle Copy

The Kindle edition of my PeopleTools Tips and Techniques book is now available. Download a copy from Amazon's site here.

Thursday, July 22, 2010

The Code is now Available!

The code for my new book PeopleSoft PeopleTools Tips & Techniques is now available. There is a download link on the book's McGraw Hill page. Look for the Downloads section. Enjoy!

Monday, July 19, 2010

Comment Moderation is now On

Sigh... I really didn't want to enable comment moderation. When I post to a blog with comment moderation, I always wonder if my comment will appear. I also like to see my comments appear immediately. What if the moderator is on vacation? Unfortunately, I find myself in a position where I HAVE to enable comment moderation. Some organization has been posting pornographic links as comments on my blog and has been doing this for about six months. I have been diligent in deleting those comments, but then it occurred to me that each subscriber to a post was receiving these links as unsolicited e-mail. I WILL NOT ALLOW ANYONE TO MOLEST MY READERS IN THIS MANNER! I find it utterly distasteful and disgraceful. If someone wants to view pornography, that is their business, but I will not allow my blog to be used to tempt/lure people into pornography. The temptation is too much for some to handle. Just as alcoholism, gambling addiction, or many other social ills start as harmless entertainment, pornography can get way out of hand.

If you have been trapped by one of the e-mail comments sent from my blog and need help, I want to provide you with some resources. I think it is the least I can do. I have no experience in this issue, so I'm just listing what I googled on the topic: Abuse and Addiction: Pornography and Cybersex and Dads.org.

Yes, you can still post comments on my blog. I really, really enjoy reading and responding to comments. I learn a lot from my readers. The unfortunate side affect of this parasite is that you will have to wait for me to read and approve your comments before they appear on this site. I really apologize for this. I wish there was something else I could do.

Wednesday, July 14, 2010

The Book is Shipping, but Where is the Code?

Amazon has been shipping my new PeopleTools book for about a week now... but where is the source code? The honest truth? It is on my laptop. I am in the process of exporting the code from my test PeopleSoft instance. I am up to chapter 9. I am averaging about 2 chapters a night, so I expect to finish this week. Once I finish, I'll send it to Oracle Press and they will post it on their web site at http://www.oraclepressbooks.com/.

Update 15-July-2010: I sent the code to the publisher last night. It is now in their production department awaiting posting. I will update again when it is available.

Update: The code is now available. You can find information in this blog post.

Monday, June 21, 2010

At UKOUG With a Copy of My New Book

I am presenting at the PeopleSoft UKOUG conference this week. It is always a pleasure to catch up with my English and European colleagues. I was able to acquire an advanced copy of my new book PeopleSoft PeopleTools Tips & Techniques. I will have the book with me during the conference, and will gladly show it to anyone who asks.

Tuesday, June 08, 2010

Marketing Flyer for PeopleTools Tips Book

The marketing flyer for my PeopleSoft PeopleTools Tips and Techniques book is now available. If you are unable to see the embedded flyer below, you can download a copy from my box.net account: PeopleTools Tips and Techniques flyer.

Saturday, May 22, 2010

Enable View Source in Online HTML Editor

If you use the Pagelet Wizard or Enterprise Portal's Managed Content features, then you have likely seen the PeopleTools online rich text editor. With PeopleTools 8.50, PeopleSoft switched to the CKEditor and added rich text editor configuration options to App Designer. This allows you to turn any long text field into a rich text field (although I don't recommend doing so, as it can have a negative impact on reporting).

The former rich text editor had a view source button. For security reasons, the PeopleTools team removed the view source button from this release. If you trust the users that have access to rich text editor pages (like the pagelet wizard) and would like to re-enable the view source button, then add 'Source','-', to the config.toolbar array in your rich text editor configuration. Here is a fragment of the configuration file:

        config.toolbar =
[
['Source','-','Maximize','

Where do you find your rich text editor's configuration? The PeopleBooks appendix Creating Custom Plug-in Files for the Rich Text Editor describes how to configure rich text editors on a per-page, per-editor basis. To change the default configuration, open the ckeditor/config.js file in your webserver's domain directory. For example, if your web server domain is named portal, open $PS_HOME/webserv/portal/applications/peoplesoft/PORTAL.war/portal/ckeditor/config.js.

Accessing PeopleCode Rowsets from Java

A reader recently asked how to create instances of the Rowset class from Java. I believe the question was more about IDE and classpath setup than it was about actual Java code. But, since it can be difficult to figure out how to use PeopleCode objects in Java, I thought I would post an example:

package test.peoplecode;

import PeopleSoft.PeopleCode.Func;
import PeopleSoft.PeopleCode.Name;
import PeopleSoft.PeopleCode.Rowset;

public static String getOprDescr(String oprid) {
Name recName = new Name("RECORD", "PSOPRDEFN");
Name fieldName = new Name("FIELD", "OPRDEFNDESC");
Rowset r = Func.CreateRowset(recName, new Object[] { });

r.Fill(new Object[] { "WHERE OPRID = :1", oprid });

return (String)r.GetRow(1).GetRecord(recName).GetField(fieldName).getValue();
}
}

Notice that the first parameter to CreateRowset is a Name object and the second is an empty array. If I were creating a hierarchical Rowset (similar to a component buffer), then I would fill the array with additional Rowset objects, as described by the CreateRowset PeopleBooks entry. Another important difference between PeopleCode and Java is that the "RECORD" and "FIELD" parameters to the Name constructor must be upper case.

Here is some PeopleCode to test this example:

MessageBox(0, "", 0, 0, GetJavaClass("test.peoplecode.RowsetTest").getOprDescr(%OperatorId));

What about the IDE's Java project classpath? If your IDE supports library definitions (like JDeveloper), then add the JAR %PS_HOME%\class\peoplecode.jar as a new library and then add the library to your project.

Tuesday, April 20, 2010

FUNCLIB's and Event Scoped Variables

While writing code for my post JSON Encoding in PeopleCode, I discovered a need for transient variable persistence (acknowledged in that post). Since I originally wrote that code in a FUNCLIB, I thought I could reuse/persist my JavaObject variables by moving those two variable declarations above the function declaration. What I found was that this had no impact on the behavior of my code. The FUNCLIB function continued to initialize a new instance of my JavaObject variables on each call. Now, PeopleBooks says that JavaObject variables are treated a little differently than other variables so we should test to see if this behavior exists for regular variables, like String variables. To test this, create a FUNCLIB that contains this code:

Local string &test;

Function testval() Returns string
If (None(&test)) Then
&test = "new value";
Return "Not initialized";
Else
Return "Initialized";
End-If;
End-Function;

You can then test this code with a PSUnit test case defined as follows:

import TTS_UNITTEST:TestBase;

class Test extends TTS_UNITTEST:TestBase
method Test();
method Run();
end-class;

Declare Function testval PeopleCode JJM_SCOPE_FUNC.FUNCLIB FieldFormula;

method Test
%Super = create TTS_UNITTEST:TestBase("Test");
end-method;

method Run
/+ Extends/implements TTS_UNITTEST:TestBase.Run +/
Local number &idx;
For &idx = 1 To 10
%This.Msg(&idx | ": " | testval());
End-For;
end-method;

What does PeopleBooks say about this? What should I expect to see? Summarized, any variable declared within an event is available to all functions within that event (take me to the PeopleBooks reference for this). Given this information, the code does work... as described. The variable is accessible by the FUNCLIB function. PeopleBooks does not say the value will persist after a FUNCLIB function returns. It is important to make this distinction. Event scoped variables are accessible by all functions within an event, but they do not persist after leaving the scope of an event. In other words, once a FUNCLIB returns, the event scoped variables are discarded. Of course, if you call a FUNCLIB function from the same event that defines the FUNCLIB function, then the variable value will persist for the duration of the calling function. But if you did that, then the FUNCLIB function wouldn't really be a FUNCLIB function. It would just be a function. By definition, a FUNCLIB function is a function defined in a different event.

My conclusion: Locally scoped variables are really locally scoped. They don't maintain state when a FUNCLIB function returns. For a FUNCLIB, locally scoped variables are only relevant if you plan to call other functions within the FUNCLIB function's event from the FUNCLIB function.

Wednesday, April 14, 2010

JSON Encoding in PeopleCode

I am a big fan of the JSON.simple Java library. JSON.simple integrates well with PeopleCode. It produces flawless JSON without ugly PeopleCode Java Reflection and is compatible with Java 1.2 (for older tools versions). Yes, the object/array to JSON conversion in JSON.simple is nice, but my real reason for using a JSON library is JSON encoding. I can mock up and string together variable values to produce JSON, but my main problem is escaping strings so that they represent safe JSON data (quotes, etc). I thought the PeopleCode EscapeJavascriptString function would handle this for me, but I discovered that JSON != JavaScript. Certain character sequences, such as \' are valid for JavaScript, but invalid for JSON. After my latest tools and app upgrade, I decided to see what it would take to encode strings for JSON from PeopleCode. Here is what I created:

class JSONEncoder
method encode(&input As string) Returns string;

private
instance JavaObject &meta_chars_;
instance JavaObject &unsafe_chars_pattern_;
instance JavaObject &int_;

method init();
end-class;

method encode
/+ &input as String +/
/+ Returns String +/

Local JavaObject &matcher;
Local string &output = &input;
Local string &replacement;
Local string &match;
Local number &offset = 1;

REM ** Run lazy init if needed;
REM ** Protects against stateless PeopleCode/Stateful JVM;
%This.init();

&matcher = &unsafe_chars_pattern_.matcher(CreateJavaObject("java.lang.String", &input));

While &matcher.find()
&match = &matcher.group();

If (&meta_chars_.containsKey(&match)) Then
REM ** replace meta characters first;
&replacement = &meta_chars_.get(&match).toString();
Else
REM ** not meta, so convert to a unicode escape sequence;
&replacement = "\u" | Right("0000" | &int_.toHexString(Code(&match)), 4);
End-If;
&output = Replace(&output, &matcher.start() + &offset, (&matcher.end() - &matcher.start()), &replacement);

REM ** move the starting position based on the size of the string after replacement;
&offset = &offset + Len(&replacement) - (&matcher.end() - &matcher.start());
End-While;

Return &output;
end-method;

method init
REM ** None only works on local vars, so get a pointer;
Local JavaObject &int = &int_;

REM ** if &int has no value, then initialize all JavaObject vars;
/*
* JavaObject vars will have no value in two scenarios:
*
* 1. First use, never initialized
* 2. Think time function, global variable, anything that causes state
* serialization.
*
* The first case is obvious. The second case, however, is not. PeopleSoft
* allows you to make App Classes Global and Component scoped objects, but
* not JavaObject variables. By using JavaObject variables in Component and
* Global scope, you can get into a bit of trouble. Retesting these values
* on each use ensures they are always initialized. The same will happen if
* you use a think-time function like Prompt or a Yes/No/Cancel MessageBox.
*/
If (None(&int)) Then
REM ** Lazy initialize Integer class;
&int_ = GetJavaClass("java.lang.Integer");

REM ** Lazy initialize the regular expression;
REM ** List other unsafe characters;
&unsafe_chars_pattern_ = GetJavaClass("java.util.regex.Pattern").compile("[\\""\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]");

REM ** Lazy initialize the hashtable;
&meta_chars_ = CreateJavaObject("java.util.Hashtable");

REM ** setup meta characters;
&meta_chars_.put(Char(8), "\b");
&meta_chars_.put(Char(9), "\t");
&meta_chars_.put(Char(10), "\n");
&meta_chars_.put(Char(12), "\f");
&meta_chars_.put(Char(13), "\r");
&meta_chars_.put("\", "\\");
&meta_chars_.put("""", "\""");
End-If;

end-method;

I adapted this code from the JavaScript quote function in the json.org JSON2 JavaScript parser. Yes, this solution does still use Java (regular expressions and hexadecimal encoding), but it doesn't require external libraries. See, my real motivation was to eliminate external dependencies. I wanted code I could compile and leave in the database; code that didn't require OS file system modifications; code that would upgrade without impacting PS_HOME, psappsrv.cfg, psconfig.sh, or any other upgraded configuration file.

Why an App Class instead of a FUNCLIB? I originally wrote this code as a FUNCLIB function. Step one of the function would populate the hashtable. This meant for each function call, I would incur the overhead of creating and populating the hashtable. Since I know I will call this function multiple times while constructing a JSON string, I wanted a mechanism to persist the hashtable across function calls. An App Class's private instance variable provides this mechanism. What about Global variables? First, I have NEVER used them. Second, you CAN'T use them with variables of type JavaObject. What about serialization, scoping, and think-time functions with Java? I protect against the "First operand of . is Null" error by lazily initializing the hashtable and the regular expression. A postback will reset the JavaObject to Null, and my lazy initialization code will reinitialize it.

Tuesday, April 13, 2010

Hex Encoding Characters

Does anyone have a PeopleCode algorithm for hex encoding strings? I'm working on an escapeJSON function and would like to come up with a good way to convert unsafe characters to unicode. Here is what I've come up with, but I would like to hear other ideas:

Local JavaObject &int = GetJavaClass("java.lang.Integer");
Local string &unicode;

REM ** I hard coded the source character to A for this example;
&unicode = "\u" | Right("0000" | &int.toHexString(Code("A")), 4);

This converts "A" to \u0041. The actual Hex part is

GetJavaClass("java.lang.Integer").toHexString(Code("A"));

I don't think there is anything wrong with my solution. I am just wondering if I overlooked some PeopleCode function for displaying numbers in Hex.