Showing posts with label IScripts. Show all posts
Showing posts with label IScripts. Show all posts

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.

Friday, June 21, 2013

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.

Thursday, September 23, 2010

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

Wednesday, April 08, 2009

Using JDBC to Execute Stored Procedures with Output Parameters

PeopleSoft allows developers to execute database stored procedures using PeopleCode that resembles SQLExec("EXEC PACKAGE.PROC_NAME(:1, :2)", &bind1, &bind2);. Even though stored procedures can have input and output parameters, SQLExec discards output parameters. Several years ago I found an interesting post that described how to use DBMS_PIPE with Oracle database to return output parameters, but, it appears the author removed that post. What made the DBMS_PIPE solution so compelling was that it shared the PeopleSoft database connection. The alternative presented below, unfortunately, requires you to maintain a user name and password, preferably encrypted and stored in a secure location. Because of the class loading issues mentioned in my post Using Oracle JDBC from PeopleCode, this post uses the Oracle specific data access classes. If you use a different database, the classes will differ, but the concept is the same. Furthermore, if you use a different database and JDBC driver, you may be able to use the standard, generic JDBC classes as described in the PSST0101 post Writing to Access Databases

Before demonstrating how to call a stored procedure from PeopleCode, we need a stored procedure to call. The following PL/SQL describes a stored procedure that has two parameters: one in and one in/out. The implementation of the procedure hard codes the output value for simplicity.

CREATE OR REPLACE PACKAGE JJM_IN_OUT AS

PROCEDURE P(
IN1 IN VARCHAR2,
INOUT1 IN OUT VARCHAR2);
END JJM_IN_OUT;
/

CREATE OR REPLACE PACKAGE BODY JJM_IN_OUT AS
PROCEDURE P(
IN1 IN VARCHAR2,
INOUT1 IN OUT VARCHAR2) IS
BEGIN
INOUT1 := 'Hello World';
END P;
END JJM_IN_OUT;
/
show errors

To build this package, copy the PL/SQL above into a text editor and then run it from SQLPlus. The procedure test follows:

SQL> var out1 varchar2(100)
SQL> exec JJM_IN_OUT.P('x', :out1);

PL/SQL procedure successfully completed.

SQL> print out1

OUT1
-------------------------------------------------------
Hello World

SQL>

The following IScript demonstrates calling a procedure with in/out parameters from PeopleCode:

Function IScript_OutParms()
Local JavaObject &driver = CreateJavaObject("oracle.jdbc.OracleDriver");;
Local JavaObject &info = CreateJavaObject("java.util.Properties");

&info.put("user", "dbuser");
&info.put("password", "secret");

Local JavaObject &conn = &driver.connect("jdbc:oracle:thin:@server:1521:SID", &info);
Local JavaObject &stmt = &conn.prepareCall("{call JJM_IN_OUT.P (?,?)}");
Local JavaObject &types = GetJavaClass("java.sql.Types");

REM ** set input parameter values;
&stmt.setString(1, "aa");
&stmt.setString(2, "bb");

REM ** register the second parameter as a output parameter;
&stmt.registerOutParameter(2, &types.VARCHAR);

REM ** execute the SQL statement;
&stmt.execute();

%Response.SetContentType("text/plain");

REM ** Display the value of the second parameter;
%Response.WriteLine("JJM_IN_OUT.P output parameter value: " | &stmt.getString(2));

&conn.close();
End-Function;

After making the connection, the code prepares an SQL statement: {call JJM_IN_OUT.P (?,?)}. JDBC procedure calls use the syntax {call proc_name (?,?)} (the question marks represent the procedure's parameters).

The code above is for demonstration purposes only. Be sure to store the database user name and password in a secure manner. When executing SQL using a second connection, be sure to consider deadlock, race conditions, etc.

Thursday, March 19, 2009

Serve JSON from PeopleSoft

Last month a reader asked me for an example of serving JSON from PeopleSoft. The following IScript demonstrates how to serve JSON by printing user and role information in JSON format:

Function IScript_GetJSON
Local SQL &usersCursor = CreateSQL("SELECT OPRID, OPRDEFNDESC, EMAILID FROM PSOPRDEFN WHERE ROWNUM < 6");
Local SQL &rolesCursor;
Local string &oprid;
Local string &oprdefndesc;
Local string &emailid;
Local string &rolename;

Local boolean &isFirstUser = True;
Local boolean &isFirstRole = True;

%Response.Write("[");
While &usersCursor.Fetch(&oprid, &oprdefndesc, &emailid)
REM ** comma logic;
If (&isFirstUser) Then
&isFirstUser = False;
Else
%Response.Write(", ");
End-If;

%Response.Write("{""OPRID"": """ | EscapeJavascriptString(&oprid) | """, ""OPRDEFNDESC"": """ | EscapeJavascriptString(&oprdefndesc) | """, ""EMAILID"": """ | EscapeJavascriptString(&emailid) | """, ""ROLES"": [");

&rolesCursor = CreateSQL("SELECT ROLENAME FROM PSROLEUSER WHERE ROLEUSER = :1 AND ROWNUM < 6", &oprid);
&isFirstRole = True;

While &rolesCursor.Fetch(&rolename);
REM ** comma logic;
If (&isFirstRole) Then
&isFirstRole = False;
Else
%Response.Write(", ");
End-If;

%Response.Write("""" | EscapeJavascriptString(&rolename) | """");
End-While;

&rolesCursor.Close();
%Response.Write("]}");
End-While;

%Response.Write("]");
&usersCursor.Close();

End-Function;

The code above uses embedded SQL. In production, be sure to use App Designer SQL definitions. This code listing also embeds JSON formatting strings. As an alternative, I recommend HTML definitions and HTML bind variables. In this manner, HTML definitions serve as templates for structured JSON data.

Formatted, the output from my demo database looks like:

[
{
"OPRID": "ADRIESSEN",
"OPRDEFNDESC": "Anton Driessen",
"EMAILID": "ADRIESSEN@server.com",
"ROLES": [
"All Processes",
"All Query Access Groups",
"EPM Scorecard Viewer",
"Portal User",
"Query Access - All FSCM"
]
},
{
"OPRID": "ADUPOND",
"OPRDEFNDESC": "Alain Dupond",
"EMAILID": "ADUPOND@server.com",
"ROLES": [
"All Processes",
"All Query Access Groups",
"EPM Scorecard Viewer",
"Portal User",
"Query Access - All FSCM"
]
},
{
"OPRID": "AEGLI",
"OPRDEFNDESC": "Anna Egli",
"EMAILID": "AEGLI@server.com",
"ROLES": [
"All Processes",
"All Query Access Groups",
"EPM Scorecard Viewer",
"Employee Global Payroll",
"Portal User"
]
},
{
"OPRID": "AERICKSON",
"OPRDEFNDESC": "Arthur Erickson",
"EMAILID": "AERICKSON@server.com",
"ROLES": [
"Accounts Payable Manager",
"All Processes",
"All Query Access Groups",
"Application Homepages",
"EP General Options"
]
},
{
"OPRID": "AFAIRCHILD",
"OPRDEFNDESC": "Alison Fairchild",
"EMAILID": "AFAIRCHILD@server.com",
"ROLES": [
"Applicant",
"All Processes",
"All Query Access Groups",
"EPM Scorecard Viewer",
"Employee ELM"
]
}
]

IScripts provide a secure free-form mechanism for serving data. This makes them perfect for serving JSON in response to a logged in user's AJAX request. If you want to serve PeopleSoft data in JSON format for consumption in a page outside PeopleSoft, then try using an Integration Broker synchronous message handler.

If you need help prototyping your JSON, take a look at the JSON homepage and JSONLint, an online JSON validator. I rely heavily on JSONLint when prototyping JSON.

Thursday, January 08, 2009

Exporting Attachments Part 2

In my post Export PeopleSoft Attachments using PL/SQL, I mentioned that PeopleSoft provides the File Attachment API for storing, retrieving, and viewing file attachments. These API functions comprise the recommended method for working with attachments. If you want to process an attachment, use the GetAttachment function to move that attachment into a file so you can access it from your app server. The ViewAttachment function, on the other hand, will send a copy of an attachment to a client browser. The ViewAttachment function is the only method provided by the Attachment API that allows a user to view the contents of an attachment. What if you want more control over the way PeopleSoft displays attachments? For example, let's say you use the File Attachment API to allow selected users to upload audio files (news, recorded training sessions, etc) and you have another page that allows other users to listen to those recordings. Should the "view" page display a "View Attachment" link or should it play the selected clip as embedded audio?

If you store attachments in the database using a RECORD:// style URL, then you can extract those attachments using PeopleCode. Here is an IScript that demonstrates this:

Function IScript_GetAttachment()
Local any &data;
Local string &file_name = "attachment.doc";
Local SQL &cursor = CreateSQL("SELECT FILE_DATA FROM PS_EO_PE_MENU_FILE WHERE ATTACHSYSFILENAME = :1 ORDER BY FILE_SEQ", &file_name);

REM Set the following header if you want a download prompt. Don't set it if you want inline content;
%Response.SetHeader("content-disposition", "attachment;filename=" | &file_name);

While &cursor.Fetch(&data);
%Response.WriteBinary(&data);
End-While;
End-Function;

This method allows you to embed audio/video, provide user configurable background images, display inline PDF files, etc.

If you follow my blog closely, you may remember that my IScripts post claims there is no way to write binary data to an HTTP response. Notice the use of the %Response.WriteBinary method above? I was wrong. I was reading the comments of the post Browse the App Server Filesystem via iScript and noticed Joe's reference to %Response.WriteBinary(). Even though it isn't documented in PeopleBooks, I tried it and it worked. Thanks Joe!

Note that I hard coded my SQL statement. I did this for simplicity in this blog post. For production systems, I encourage you to use SQL definitions. Of course, if you implement this solution, you will need to change the SQL select table name to the name of your attachment table. Likewise, you will probably derive your file name from a parameter rather than hard code it as shown here.

As with any custom development, make sure you write secure code. For example, when executing SQL based on parameters, use SQL binds. Failure to use SQL binds can result in SQL injection flaws. Likewise, if you accept an attachment from a user and then display that attachment using a means other than the provided ViewAttachment function, then make sure you either validate that input or validate the output. For example, let's say you have a page that allows users to upload JavaScript or other HTML that will be executed on other pages. Failure to restrict this type of usage provides malicious users with an opportunity to create HTML injection and cross site scripting vulnerabilities.

Tuesday, January 06, 2009

Serve those JavaScript Libraries Quickly... and Safely

If at all possible, don't serve JavaScript libraries from PeopleSoft applications (dynamic PeopleCode). The best place to serve a JavaScript library is from a static file residing on the PeopleSoft web server. Many PeopleSoft developers, however, don't have access to their web server file systems. The next logical place to store a JavaScript library is in an HTML definition in app designer. This is where the PeopleTools developers store JavaScript libraries and, therefore, is the preferred location for small JavaScript libraries. Unfortunately, the maximum size of a PeopleTools 8.4x HTML definition is too small to fit a good JavaScript library like jQuery. Enter John and Derek. John and Derek both wrote about a workaround to this limitation. Rather than using HTML Definitions, these innovative developers used the message catalog to store their JavaScript libraries (jQuery, of course). Here are links to those blog posts:

Both of these posts provide an alternative for PeopleSoft developers that want to use a JavaScript library but don't have access to their PeopleSoft web servers' file structure. Building upon John's and Derek's approach, here are some ideas to help improve performance.

Packing/Minification/Raw

Based on Julien Lecomte's post Gzip Your Minified JavaScript Files, we can see that Dean Edwards's Packer algorithm produces the smallest compressed file. As Julien notes, however, the benefit of that smaller download is offset by a corresponding increase in execution/evaluation time. Julien points out that other compression techniques like Douglas Crockford's JSMin and Yahoo's YUI Compressor produce larger files that evaluate much faster. Therefore, when using JavaScript compression alone, you, as an analyst or developer, need to determine which method is appropriate for your implementation. Do you have low bandwidth connecting powerful computers to your PeopleSoft instance? If so, then maybe the Packer algorithm is more appropriate for you. If you have good bandwidth (or low bandwidth and low power client computers), then maybe you should consider JSMin or the YUI Compressor. Both of these compression techniques produce compressed text files that can be stored in a message catalog entry as described by John and Derek.

Caching

Browsers are designed to cache static, externally referenced resources. The methods demonstrated in the previously mentioned blogs insert the contents of a JavaScript library into the body of an HTML page. By inserting the contents directly into a page, the browser is not able to cache it with other JavaScripts, stylesheets, and images. An alternative approach is to serve JavaScript libraries stored as message catalog entries from IScripts. Using this approach, you could reference your JavaScript library using HTML like:

<script type="text/javascript" language="JavaScript" src="/psc/portal/EMPLOYEE/EMPL/s/WEBLIB_JS_LIBS.ISCRIPT1.FieldFormula.IScript_jQuery">
</script>

The PeopleCode for this IScript would look something like:

Function IScript_jQuery()
Local string &jq = MsgGetExplainText(28000, 1, "alert('Unable to load the jQuery JavaScript library!\n\nThe Message Catalog entry (28000, 1) does not exist.');");
%Response.SetHeader("Cache-Control", "max-age=28800, proxy-revalidate");
%Response.WriteLine(&jq);
End-Function;

Update November 23, 2009: PeopleSoft actually overwrites the Cache-Control header. I have not found a way to set this through an IScript. The only solution I have found to cache IScript content is to use a ServletFilter to set the Cache-Control header after PeopleSoft returns a response.

The benefit of this approach is that users only download the JavaScript library once, rather than once per page visit. Considering cache, file size is not as important as execution time. Because the Packer algorithm produces a smaller file size, it excels in low bandwidth scenarios. With caching, however, the one time bandwidth hit might not compensate for the on going evaluation cost of the Packer algorithm. If you cache your JavaScript library, then a minification algorithm like JSMin or YUI Compressor may provide better performance.

GZip Compression

The first column of stats on Julien Lecomte's matrix compares the size of jQuery when packed, minified, or YUI Compressed. The second column compares that same content GZip compressed. GZip compressed, all three JavaScript compression methods result in a fairly comparable download size. When GZipped, the main difference between JSMin/YUI and Packer is the browser evaluation time required to "unpack" the packed JavaScript library.

The next performance improvement to explore with our IScript scenario is GZip compression. Fortunately, PeopleSoft takes care of GZip compression for us. If you look at your Web Profile settings, you will notice a check box labeled Compress Responses. If you enable this setting, PeopleSoft will automatically GZip compress the contents of your IScripts (and the contents of any other PeopleSoft page).

Whatever approach you take, be sure to consider security. OWASP listed Injection flaws as the Number 2 security concern in the OWASP 2007 top 10. Injection isn't limited to SQL. If you take user entered values and insert them into the HTML of a page, unescaped, then you have given that user the ability to inject malicious content into a page. If you use a message catalog approach and insert the contents of a message catalog entry into a page, then be sure to secure your message catalog. Failure to do so could have the same impact as the number one security threat in the OWASP 2007 Top 10: Cross Site Scripting (XSS)

Sunday, August 17, 2008

PeopleSoft/Calendar Integration

Last week three different people asked me if it was possible to integrate PeopleSoft with a calendar management program (Microsoft Outlook, to be specific). Whether you are trying to create appointments for HRMS Enterprise Learning classes, ELM training classes, or eRecruiting interviews, the solution is the same. Since most calendar programs support the iCalendar (ics) format and since iCalendar files are text files, we can generate appointments from PeopleSoft IScripts and serve those as file downloads. As a starting point, we can copy the event example from the iCalendar RFC:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
BEGIN:VEVENT
DTSTART:19970714T170000Z
DTEND:19970715T035959Z
SUMMARY:Bastille Day Party
END:VEVENT
END:VCALENDAR

Modifying this a little, we can convert it to an HTML object with bind parameters:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My Company//PeopleCode vCal 1.0//EN
BEGIN:VEVENT
DTSTART:%Bind(:1)
DTEND:%Bind(:2)
SUMMARY:%Bind(:3)
END:VEVENT
END:VCALENDAR

We can serve this text to a client browser using an IScript that looks something like:

Function IScript_GetICalendarEvent
Local DateTime &startTime;
Local DateTime &endTime;
Local DateTime &tempTime;
Local string &startTimeUTC;
Local string &endTimeUTC;
Local string &eventTitle;

REM ** TODO: Initialize date and title variables from database;

REM ** change time zone to your time zone;
&tempTime = DateTimeToTimeZone(&startTime, "PST", "UTC");
&startTimeUTC = DateTimeToLocalizedString(&tempTime, "yyyyMMdd'T'HHmmss'Z'");

&tempTime = DateTimeToTimeZone(&endTime, "PST", "UTC");
&endTimeUTC = DateTimeToLocalizedString(&tempTime, "yyyyMMdd'T'HHmmss'Z'");

%Response.SetContentType("text/calendar");
%Response.WriteLine(GetHTMLText(HTML.ICAL_EVT, &startTimeUTC, &endTimeUTC, &eventTitle);
End-Function;

All you have to do is fetch your event data from the database and provide your users with a means to access this IScript. If you want your users to be able to access this IScript from a workflow event, then modify your e-mail workflow template to include a link to this IScript. Likewise, if you want your users to be able to download a calendar event from a page, add a link for this IScript to that page. When creating a link to this IScript, be sure to include all the keys required to fetch the event's data from your database.

If you study the iCalendar RFC, you will notice that it also includes a specification for tasks. You could modify this example to add tasks for voucher due dates, etc. I'll leave the possibilities and implementation to your imagination.

This example is only meant to be a starting point. Since iCalendar support has many potential uses in PeopleSoft, I would create a reusable App Class API for rendering iCalendar (ics) files.

Tuesday, April 01, 2008

What is a WEBLIB?

I recently posted about IScripts, and in that post, I mentioned the term WEBLIB. The next logical question after "What is an IScript" is, "What is a WEBLIB?" Simply put, a WEBLIB is a container for an IScript. The term WEBLIB has a history somewhat like the Menu definition. It is a legacy artifact that really isn't necessary but continues to exist because we implemented it that way in the beginning, when the way we implemented it was the most logical way to implement it (before Application Packages, etc). A WEBLIB is similar to a FUNCLIB. The only difference between a WEBLIB and a FUNCLIB is the name... and the way PeopleSoft treats the code within the record defintion. Just like a FUNCLIB, something you studied in PeopleCode class, a WEBLIB is a Derived/Work record definition with a special name. When you prefix a record with WEBLIB_, you are telling the PeopleSoft application code to allow people to access the functions defined within that record. Just like any other record defined function, functions defined in records prefixed with WEBLIB_ can be declared and called from any PeopleCode. What makes WEBLIB_ functions unique is that they can also be called from a URL (see my previous post on IScripts). Therefore, the term WEBLIB describes the type of record used to store a special type of PeopleCode function, an IScript.

Because WEBLIB's can be accessed from a browser using a specially formed URL, we need to secure them. Just like standard components, you define WEBLIB security on permission lists. When you open a permission list, you will notice a link across the bottom titled Web Libraries. From this tab, you can specify which WEBLIB functions a permission list (and, indirectly, a user) can access. This is where the magic of the record name prefix appears: only records prefixed with WEBLIB_ can be added to the Web Libraries section of the permission list.

One of the frustrations of the WEBLIB design is the record name requirement. Generally speaking, a PeopleSoft implementation will involve custom record development. An implementation team usually prefixes these custom record names with a company or team abbreviation. This helps the team distinguish custom tables from delivered tables. Because WEBLIB's must start with the prefix WEBLIB_, this is not possible. Instead, the implementation, or development, team is required to move the team specific prefix to the next component of the record name, the part following the WEBLIB_. Now, if the team prefix is 3 characters long including an _ (for example, ADS_), then the developer only has 4 characters left to uniquely and accurately describe this WEBLIB's purpose. Why? Because a record definition can only contain 15 characters and the first 11 are used by system and team required prefixes.

To demonstrate, let's look at a delivered WEBLIB, WEBLIB_QUERY. WEBLIB_QUERY is a Dervied/Work record that you can open in Application Designer. When you open this record, you will notice that it contains 3 fields with PeopleCode. The first 2 fields contain IScript functions, and the 3rd, QRYGENFUNCS, contains FUNCLIB, not ISCRIPT, functions. The difference between the functions in the 3 fields has to do with the function prefix, not the field names. To make a PeopleCode function available from a URL, the function not only has to be contained in a record definition prefixed with WEBLIB_, the specific function name must also be prefixed with IScript_. To see an example of WEBLIB permissions, open the Permission List PTPT100 and switch to the Web Libraries tab. From here, you can see WEBLIB_QUERY and the 2 IScripts contained within that WEBLIB.

Sunday, February 10, 2008

What is an IScript?

It has been a few years since I attended my first PeopleSoft|Connect conference. As a new PeopleSoft developer, I was eager to learn all I could about PeopleSoft's Integration Broker, PeopleTools, and PeopleSoft's web UI. I spent hours pestering the experts (ask Robert Taylor, a prior Integration Broker product manager, and Tushar Chury, a former component interface developer). During one of the sessions I attended, someone from the audience asked a question about integration and the presenter replied, "I would probably create an IScript to do that." I don't remember the question. The only thing that stuck with me was the term "IScript." I had so many other questions for the experts that I didn't ask what an IScript was. Nevertheless, I left the event with a big question: "What is an IScript?" Since I had already taken the PeopleTools and PeopleCode classes and had not heard mention of IScripts, I decided they must be some legacy PeopleSoft technology that I didn't need to learn. To satisfy my curiosity, I looked up IScripts in PeopleBooks and received a... well... thorough definition... just like reading a good dictionary (see Enterprise PeopleTools 8.49 PeopleBook: PeopleCode API Reference > Internet Script Classes (iScript)). Yes, after reading the definition in PeopleBooks, I knew what an IScript was, but I just couldn't figure out why I would want to use one. Now, several years later, I find that I can't work without IScripts. Ajax, pagelets, WML, and SVG provide excellent use cases for IScripts. Each of these "technologies" requires marked up text without the overhead of the PIA rendered component HTML.

I like to think of IScripts as the PeopleTools developer's Swiss Army Knife. Besides a "cutting implement" a Swiss Army Knife might have a leather punch, a screw driver, a can opener, etc. Yes, you can do just about anything with a Swiss Army knife, but it might take you a long time to get the job done. Then, when you are done, the job might not be as satisfactory as it would have been if you had used the right tool. Have you ever opened a can with a Swiss Army Knife can opener? I'll bet you worked up an appetite. Likewise an IScript is the right tool for some jobs, but choose wisely. You can probably replicate the postback behavior of a component with an IScript, but it is a lot of unnecessary work. Likewise, you may have to consider multi-lingual translations, etc.

I compare IScripts to JSP or ASP. Basically an IScript is PeopleCode that has access to the Request and Response objects. Just like JSP and ASP, you, the developer, writes code to read parameters from the request and write information, data, etc to the response. Unfortunately, just like ASP, the response object's write methods only render text. Since the response object does not contain any binary write methods, you will have to ignore the dynamic image generation possibilities (I was hoping to use IScripts to render images stored in a user table, but I haven't figured out how to make that work since the write method only accepts plain text).

How do you create an IScript? For more information, you can look it up in PeopleBooks, but just to vaguely satisfy your curiosity, IScripts follow the same design pattern as FUNCLIBS. An IScript is a PeopleCode function stored in a record definition. The record definition name must start with WEBLIB_. The function can be in any field event of the record definition. By convention, we generally use the field ISCRIPT1 and the event FieldFormula. The function name, however, must start with IScript_ and cannot take any parameters or return a value.

How do you call an IScript? IScripts can be called a number of ways. How you call it depends on the purpose of the IScript. If your IScript is a pagelet, then you will create a CREF for your IScript in the portal registry under Portal Objects > Pagelets. If your IScript is called from JavaScript (Ajax, etc), then you call your IScript using a URL like http://server:port/psc/site_name/portal_name/node_name/s/WEBLIB_name.FIELDNAME.FieldFormula.IScript_name. To make it easier, you can generate an IScript URL using the PeopleCode built-in function GenerateScriptContentURL.

Now that you know what an IScript is, you might be interested in looking at some examples. Chris Heller posted a couple of IScripts that can be used as Bookmarklets. These Bookmarklets allow us, the developer, to exend our existing tool set by writing tools that we can use where we work: in the web browser. On Wednesday, April 4th, 2006, Chris posted a bookmarklet/IScript that will drill into a portal registry content reference from a PeopleSoft page. In his 2006 Open World and 2007 Alliance presentations, Chris showed us how to use IScripts and bookmarklets to turn on tracing and display security information. ERP Associates also has some good PeopleSoft Bookmarklet examples.

Thursday, January 31, 2008

%Component Gotcha

I was just writing an IScript for the Approval Workflow Engine (AWE) that called the Approval Manager class to approve a transaction. The transaction failed because the Application Classes used in the Event notification handler used the %Component system variable. This would have been fine except %Component cannot be called from an IScript. To work around this, I wrapped the calls to %Component in a function that checks whether the execution context is a component. Here is the code:

Function GetComponentName() Returns String
If (%ContentType = "c") Then
Return %Component;
Else
Return "";
End-If;
End-Function;

If you are writing your code in a Page or a Component and use the %Component system variable, then you shouldn't have any problems. But, if your code is in a FUNCLIB or Application Class, a reusable component, then be aware that one of those "reuses" might be from PeopleCode that runs outside a Component (IScript, AppEngine, Message Subscription, etc). If that is the case, then please be kind to the developer that follows you by wrapping your calls to %Component in a function that tests to see if your code is executing within a Component.

%Request.GetParameterNames

I was working on an IScript that wrote WML to the browser and needed to pass the query string parameters back to the client as postfields. I thought I would just loop through the %Request.GetParameterNames array and write those parameters back to the client marked up as postfields. When I did this I saw 6 additional parameters:

PSHome
Portal
Node
ContentType
ContentID
ICScriptName

To test this, run the following IScript:

Function IScript_TestParams
Local array of string ¶ms = %Request.GetParameterNames();
Local string ¶mName;
Local number ¶mIdx;

%Response.SetContentType("text/plain");

For ¶mIdx = 1 To ¶ms.Len
¶mName = ¶ms [¶mIdx];
%Response.WriteLine(¶mName | ": " | %Request.GetParameter(¶mName));
End-For;

End-Function;

Now, the part that really, really threw me was how PeopleSoft interpreted the ICScriptName parameter. The code I wrote copied the request parameters to the response as postfields and then posted them back to a different IScript on the same server. After a good hour of troubleshooting, trying to figure out why the browser never displayed the second IScript, I realized that the psc servlet was using the IScript from the ICScriptName parameter, not the IScript in the main URL. In fact, modifying the URL a little bit, I noticed that you can leave off the Record.Field.Event.IScript_Name portion of the URL in favor of ?ICScriptName=Record.Field.Event.IScript_Name (not that you would want to do that).

So what now? How can we work around this %Request.GetParameterNames dilemma? Here is a function that will parse the %Request.QueryString into key/value pairs and print them to the browser:

Function IScript_TestParams2
Local array of string ¶ms = Split(%Request.QueryString, "&");
Local array of string &keyValue;
Local number ¶mIdx;

%Response.SetContentType("text/plain");

For ¶mIdx = 1 To ¶ms.Len
&keyValue = Split(¶ms [¶mIdx], "=");
%Response.WriteLine(&keyValue [1] | ": " | Unencode(&keyValue [2]));
End-For;

End-Function;

Of course, this function does not account for parameter arrays, etc. I'll leave that up to you.

Caveat: I was working on a PeopleTools 8.48.07 instance. I have no idea if this behavior is the same on other PeopleTools versions