Thursday, May 08, 2008

Corrections for Yesterday's Custom Transformer Post

If you read yesterday's post, Simplify HTML Pagelet Migration with a Custom Transformer, sometime between 10:54PM PDT yesterday and 11:21AM PDT today, I apologize. This morning I noticed I had forgotten to post the last step in configuring a custom Transformer, the step where you associate the Transformer with a data type. For my observant readers, you will probably notice that I also fixed a few minor grammatical errors. Rather than post corrections, etc, that will be difficult to associate to the original document, I just corrected the document in place. I replaced the original post with the corrected version.

Wednesday, May 07, 2008

Simplify HTML Pagelet Migration with a Custom Transformer

Generally speaking, IT organizations like to keep development code separate from production code. When development code is ready for production, good change management rules dictate a graduation/migration path, usually something like DEV > TEST > QA > PRO. Likewise, on occasion, a development team may request a fresh copy of a production database. With PeopleTools managed objects, we can easily migrate Application Designer projects between our environments without much concern for environment specific code. Pagelet Wizard created pagelets, however, are not managed objects and need to be migrated using Data Mover Scripts. Some pagelets, like Query pagelets, require managed objects in order to function. Other pagelets, like HTML pagelets may contain hard coded URL references to other DEV/TEST/QA/PRO servers. How can we effectively manage these URL's? Can we, as PeopleSoft developers, automate the process for updating these URL's?

As we know from experience, HTML data sources typically use the Passthru display format. Passthru means the HTML content from step 2 is displayed in the Pagelet without any processing or modification. Therefore, we can't leverage Meta-HTML or any other PeopleTools functionality to simplify the management of our HTML based pagelet. We can, however, create a new transformer and display type and have this new transformer perform additional processing to help us resolve this URL maintenance problem.

Now that we have a method for implementing additional processing, how shall we maintain the actual URL's? I recommend using node definitions. Node definitions contain a portal tab where we can enter a content and a portal URL. As delivered, PeopleTools uses these node URL's when creating real URL's from portal registry content references (CREF's). Using a custom transformer, we can leverage this existing Meta-data to dynamically generate the URL's included in HTML Pagelets.

What we need is a custom Transformer that can convert a URL placeholder into a full URL using database driven Meta-data. Keeping with PeopleTools convention, the solution below will demonstrate the creation of a transformer that resolves custom Meta-HTML tags. This custom transformer will expand the text %NodePortalURL(NODE_NAME) into the Portal URL for that node. This will allow us to write Pagelet Wizard HTML like:

<img src="%NodePortalURL(NODE_NAME)/images/dynamic-chart.png"/>

And our custom transformer will translate this HTML into:

<img src="http://other.server.name:8080/images/dynamic-chart.png"/>

To implement this solution we need an Application Package to store our custom App Class. For demonstration purposes, we will call this Application Package CUSTOM_TRANSFORMERS. I expect in your environment, you will prefix this new Application Package with your site specific prefix. In this new Application Package, we will add a new package called Transform. And, to this new package, we will add the class NodePortalUrlTransformer. This new class, NodePortalUrlTransformer will contain the code required to implement our URL transformation feature. The path for our new Application Class should look something like CUSTOM_TRANSFORMERS:Transform:NodePortalUrlTransformer. Here is the code you will need to paste into this new Application Class:

import PTPPB_PAGELET:UTILITY:*;
import PTPPB_PAGELET:Transformer:*;
import PTPPB_PAGELET:*;

/**
* Transforms Text by replacing %NodePortalURL(NODE_NAME) with the Portal URI
* of the node NODE_NAME.
*/
class NodePortalUrlTransformer extends PTPPB_PAGELET:Transformer:Transformer
method NodePortalUrlTransformer(&ID_param As string);
method execute(&pageletID As string) Returns string;
method Clone() Returns PTPPB_PAGELET:Transformer:Transformer;
end-class;

/**
* Constructor.
*
* @param id_param ID of this object. Should be unique.
*/
method NodePortalUrlTransformer
/+ &ID_param as String +/
%Super = create PTPPB_PAGELET:Transformer:Transformer(&ID_param);
end-method;

/**
* Replaces%NodePortalURL(NODE_NAME) with the Portal URI
* of the node NODE_NAME.
*
* @param pageletID ID of the pagelet being executed.
*/
method execute
/+ &pageletID as String +/
/+ Returns String +/
/+ Extends/implements PTPPB_PAGELET:Transformer:Transformer.execute +/
Local JavaObject &pattern;
Local JavaObject &matcher;
Local string &sourceText = %This.DataToTransform.Value;
Local string &transformedText = &sourceText;
Local string &nodeName;
Local string &nodeUrl;

REM ** Resolve %NodePortalURL tags;
&pattern = GetJavaClass("java.util.regex.Pattern").compile("(?i)%NodePortalURL\((\w+)\)");
&matcher = &pattern.matcher(CreateJavaObject("java.lang.String", &sourceText));
While &matcher.find()
&nodeName = &matcher.group(1);
If (&nodeName = "LOCAL_NODE") Then
&nodeName = %Node;
End-If;
SQLExec(SQL.GET_NODE_URI, &nodeName, "PL", &nodeUrl);
&transformedText = Substitute(&transformedText, &matcher.group(), &nodeUrl);
End-While;
&pattern = Null;
Return &transformedText;
end-method;

/**
* Make an exact copy of this object.
*
* @return Text Exact copy of this object
*/
method Clone
/+ Returns PTPPB_PAGELET:Transformer:Transformer +/
/+ Extends/implements PTPPB_PAGELET:Transformer:Transformer.Clone +/
Return create CUSTOM_TRANSFORMERS:Transform:NodePortalUrlTransformer(%This.ID);
end-method;

Keeping with good PeopleTools coding practices, the code above references a managed SQL object named GET_NODE_URI. Here is the SQL for that SQL definition:

SELECT URI_TEXT 
FROM PSNODEURITEXT
WHERE MSGNODENAME = :1
AND URI_TYPE = :2

Now that we have our code in place, we need to register our new Transformer so we can use it in our HTML pagelets. Rich Manalang wrote up an excellent transformer tutorial following the same steps, but his includes pictures. You can find a link to his tutorial below.

We will start by registering the Transform Type. Navigate to:

Portal Administration > Pagelets > Pagelet Wizard > Define Transform Types > Add

On this page, you will need to give your Transform Type a name, a description, and specify the implementing Application Class. If you have been following this example, then use the following values:

Transformation Type: NODE_PORTAL_URL
Description: Resolve Node URL Meta-HTML
Package Name: CUSTOM_TRANSFORMERS
Path: Transform
Application Class ID: NodePortalUrlTransformer

Next, you will need to define a Display Format that corresponds to your new Transform Type. Navigate to:

Portal Administration > Pagelets > Pagelet Wizard > Define Display Formats > Add

You can use the following values to define your new display format. Again, the Define Display Formats graphic on Rich's post applies. In fact, the only values you need to change are the Display Format ID, Description, and Transform Type. For reference, I've repeated the values below:

Display Format ID: NODE_PORTAL_URL
Description: Resolve Node URL Meta-HTML
Transform Type: NODE_PORTAL_URL
Page Name: PTPPB_WIZ_DISP_PST
Package Name: PTPPB_PAGELET
Path: TransformBuilder
Application Class ID: PassthroughBuilder

The last step is to associate our new Display Format with the HTML Data Type. Navigate to:

Portal Administration > Pagelets > Pagelet Wizard > Define Data Types

Select the HTML Data Type. At the bottom of this page, in the section titled Display Formats to use with this Data Type, we need to add our new Display Format: NODE_PORTAL_URL. After you save, your configuration and development for your new transformer is complete. You may now use this new Meta-HTML character sequence in your Pagelet Wizard HTML Pagelets.

The Enterprise Portal's Pagelet Wizard is one of my favorite productivity and usability tools. One of it's key features is its extensibility. The Pagelet Wizard contains components that allow you to register your own data sources, display types, and transformations. Likewise, the Internet Technology PeopleBook contains a section devoted to Pagelet Wizard configuration, customization, and usage. If you prefer examples over PeopleBooks, you can open and view the delivered Pagelet Wizard Application Packages in Application Designer. If you are interested in creating your own data source, tranformer, or display type, you may find a delivered Application Class you can copy and modify.

Additional resources:

Monday, May 05, 2008

PIA_KEYSTRUCT JavaScript Object

I was just looking at the JavaScript that PeopleSoft includes in each PIA page and noticed a JavaScript object I have not seen before: the PIA_KEYSTRUCT object. It looks like this object contains key/value pairs for each level 0 search key, primary and alternate. What can you do with this? I'm not sure yet. I've been thinking about it for a couple of weeks. My two main questions:

  1. Can I use this to create new developer tools (bookmarklets, etc)?
  2. Can I use this to empower users?

For now, the only example I can show you is how to open a query and pass in the PIA_KEYSTRUCT values as query bind parameters. This example passes the Role Administration component's ROLENAME key to a delivered query that displays the role's permission lists. Here is the JavaScript:

window.open("/psc/portal/EMPLOYEE/EMPL/q/?ICAction=ICQryNameURL=PUBLIC.PT_SEC_ROLE_CLASS&BIND1=" + escape(frames['TargetContent'].PIA_KEYSTRUCT.ROLENAME))

In the example above, I navigated to PeopleTools > Security > Permissions & Roles > Roles and ran this JavaScript in the Firebug console. Here is a bookmarklet that will do the same thing: Role's Permission Lists. To test this bookmarklet, right click the link and save it as a favorite/bookmark, navigate to your Role administration page, select a role, then activate your new favorite/bookmark. One of the great features about bookmarklets is that they can be added to your browser's links toolbar to "extend" the functionality of your browser. Unfortunately, this particular example is page specific. This particular bookmarklet and code will only work if you are on the PeopleTools Role administration page. Furthermore, you can provide the same functionality by combining PeopleCode with JavaScript in an HTML area or, even better, using a Pushbutton/Hyperlink on a PIA page with full access to the level 0 search keys (in other words, this is a great pedagogical example with little practical value).

Really, as developers, can we obtain practical value from this little piece of JavaScript that the PeopleTools developers generously provided for us? I think so. Here are a couple of ways I think we can leverage this piece of JavaScript:

  • Enhance the Ctrl-J page by adding the level 0 search keys
  • Simplify the Pagelet Wizard link creation process

Collaborate '08 Presentation Available

Quest's Collaborate '08 presentations are now available for download. You can download the presentation I gave from the Quest website: PeopleTools Advanced Tips and Techniques. Our PDF printer software prints landscape, so you will want to rotate the presentation clockwise for optimal viewing.