Tuesday, January 06, 2009

Injecting JavaScript Libraries into PeopleSoft Pages

Before you start creating Ajax and Dynamic HTML usability enhancements for your PeopleSoft applications, I recommend learning a good JavaScript library. When creating client side usability enhancements, JavaScript libraries significantly reduce the amount of JavaScript you have to write and maintain. The first page of a Google search for JavaScript libraries should display several alternatives. Wikipedia also maintains a list of JavaScript libraries (my favorite JavaScript library is jQuery). After you select a JavaScript library, read the library's documentation, read some tutorials, and write some static HTML test pages. It is a good idea to get to know your chosen JavaScript library before you try to integrate it with PeopleSoft.

After you find and become proficient with a good JavaScript library, you will need a way to inject that library into your PeopleSoft application. If you limit your Ajax/DHTML plans to modifying a select number of delivered pages or enhancing a custom page, then you can inject your JavaScript library by adding a new HTML Area to your target page with the appropriate <script> tag pointing to the location of your JavaScript library. If your plans call for adding a generic usability enhancement to every page, then you will want a different solution. It is not practical or advisable to modify every page of your PeopleSoft application.

One way to make a JavaScript modification available to all pages within your application is to find an Application Designer managed object that can act as a vehicle, carrying that JavaScript modification to the client browser. We could then modify that object, injecting our JavaScript library into all PeopleSoft pages. Now that we have a potential solution, let's use our analytical skills to try to find a managed object that is common to all PeopleSoft pages. An analyst is a lot like a detective; investigating computer systems looking for patterns and solutions. Like any mystery, let's start with what we know - the facts. One thing we know is that the PeopleSoft user interface is comprised of HTML, JavaScript, CSS, and images. Using our browser we can view the source of the PeopleSoft generated HTML, CSS, and JavaScript, looking for patterns and other clues. Browser based tools like Firebug dramatically simplify this type of investigation, allowing us to view all of a page's JavaScript and CSS resources from a drop-down list. Using Firebug, we can see that the cs servlet serves many of the images, CSS, and JavaScript resources used by PeopleSoft pages. Since we know that the PeopleSoft application compiles CSS files from App Designer stylesheets and that images served by PeopleSoft come from Image Definitions stored in App Designer, it stands to reason that those JavaScript files served by the cs servlet would also come from managed objects in App Designer. Making the case stronger, we can see that PeopleSoft applications store JavaScript files in the web server cache directory, the location for all client-side managed objects.

Based on the previously established anecdotal evidence, we will assume that JavaScript files are managed object. If this is true, the next logical question to ask is, "What type of managed object?" Just as CSS files translate to Stylesheet managed objects, there must be some type of managed object that contains JavaScript. Searching PeopleBooks for JavaScript, we can see that the function GetJavaScriptURL() serves an HTML Definition as a JavaScript file. Based on this information, we form the following hypothesis:

JavaScript files served by the cs servlet are HTML Definitions and can be modified in App Designer like any other HTML Definition.

To test this, we can copy the name of a JavaScript file, like PT_PAGESCRIPT, and try to open that item as an HTML Definition. This test passes. Should we now conclude that JavaScript files served by the cs servlet are HTML Definitions? Two more tests:

  1. Compare the HTML Definition to the downloaded JavaScript file. Don't expect a perfect match. The HTML Definition may contain Meta-HTML whereas the downloaded JavaScript file will contain the Meta-HTML's resolved value.
  2. Modify an HTML Definition and check the results.

Using a file diff utility like WinDiff or jEdit's JDiffPlugin, we can compare the downloaded JavaScript file to the contents of the PT_PAGESCRIPT HTML Definition.

To satisfy the modification test, I suggest adding a short comment to the end of PT_PAGESCRIPT, something like /* XXX */. Since both of these tests pass, we have very strong evidence that JavaScript files served by the cs servlet are, in fact, HTML Definitions and can be modified using App Designer. Based on this investigation, we have discovered an object type that can we can use as a vehicle to carry our global customizations to our users' browsers. Since we have been using PT_PAGESCRIPT for testing, it would seem logical to continue with that HTML Definition, customizing it as needed to add additional JavaScript based DHTML usability enhancements. I tried this once. After writing a few lines of code and saving, App Designer displayed an error telling me that the HTML Definition had exceeded the maximum size and would be truncated. Generally speaking, the actual size limitation is not relevant. What is relevant is that we know a size limitation exists. Considering the size of PT_PAGESCRIPT without any modifications, there is no room for us to add additional JavaScript to PT_PAGESCRIPT. Looking through the list of JavaScript files common to all PeopleSoft pages, I suggest PT_COPYURL. PT_COPYURL appears to contain the JavaScript required to make the copy URL button work. The copy URL button is that double paper/carbon copy button in the page bar at the top of most PeopleSoft pages. I don't think early 8.4x versions of PeopleTools had this button. I don't remember when it was added, but I do remember seeing it as early as PT 8.46. If you have that button, then chances are, you have the PT_COPYURL HTML Definition. If you don't, then you may have to find a different HTML Definition to modify.

Once you identify an HTML Definition, add JavaScript similar to the following to the end of the delivered HTML Definition:

/* Conditionally include a JavaScript/Ajax libary */
if(!window.jQuery) {
document.write("<scr" + "ipt id='jq' " + "src='/scripts/jquery.js'><\/script>");
}

/* Unconditionally insert a JavaScript file */
document.write("<scr" + "ipt id='xxx_ui' " + "src='/scripts/ui.js'><\/script>");

The first 3 lines demonstrate how to insert a static JavaScript file into all PeopleSoft pages conditioned upon the existence of an object. We typically display PeopleSoft pages in a frameset where the content frame only contains the HTML, JavaScript, and CSS required for that page. It is possible, however, to use a display template that proxies a page's content into the same HTML page as the header. The HOMEPAGE_DESIGNER_TEMPLATE is an example of this type of HTML template. If your header also contains a reference to your JavaScript library, then, best case, you will have multiple instances of your JavaScript library in memory. Worst case, your page will quit working. One way to work around this issue is to test for the existence of your JavaScript library prior to inserting it into a page. Since I use jquery, my code tests for the existence of the jQuery object.

The last line of the example above shows how to blindly insert a static JavaScript file into a PeopleSoft page. This approach works well for static JavaScript that you know isn't used by your header.

If you don't have access to your web server to install static JavaScript files or if your JavaScript needs to be dynamic, then take a look at John's post AJAX and PeopleSoft. In the post and comments, you can read about alternative ways of storing and serving JavaScript to PeopleSoft pages.

Changing a delivered HTML Definition is considered a modification. Like all modifications, you will need to consider compatibility and upgrade issues. To manage this modification through PeopleTools upgrades and patches, make sure you adequately document your modifications with code comments, project comments, and additional project management documentation. When considering upgrades, your documentation goal is to identify your modification and point the person applying an upgrade to any documentation related to this modification. Because of size limitations, you may not be able to document your entire modification inline. You will, however, be able to point other people at your documentation for this modification. For an effective, short, inline comment, I suggest something like:

<!% BEGIN xxx_1234, 13-DEC-2008, you@yourcompany.com -->
Your modified code goes here...
<!% END xxx_1234, 13-DEC-2008, you@yourcompany.com -->

With this comment, I have documented the start and end of this modification, the project name of the modification (xxx_1234), the date of the modification (13-DEC-2008), and the developer that made the modification (you@yourcompany.com). I have applied several patches over other developers' modified code. Without this type of START/END comment, it is impossible to differentiate between delivered code and modified code. Likewise, sharing a common prefix for all modifications (xxx_ in this case), dramatically simplifies searching for and identifying modifications.

Any time you modify a delivered object, you risk rendering that object unusable. If you modify a delivered PeopleTools object like PT_PAGESCRIPT, ensure that the delivered code works the same as it did before you modified it.

66 comments:

Thathagai said...

We have implemented a similar solution in our company a year ago. There is a small glitch to this solution. While using AJAX to inject code, the ajax part of the code gets loaded before the pagescript is loaded. This throws an error that the site accessed cannot be found.
Do you have any thoughts?

Jim Marion said...

@Nandini, When you use the technique described in this post, the inserted JavaScript may start executing immediately. It is up to you to defer your JavaScript until after the page loads. If you are using jQuery, you can defer execution using $(document).ready.

Jim Marion said...

@Nandini, if you are not using jQuery, take a look at Dean Edward's window.onload (again) post. Pay particular attention to the comments, scrolling to the end for some very recent comments.

Shiva said...

Hi Jim,

I have a query in using javascript. My requirement is when I click on a link(which is a html link), I have to save the component and after that i have to execute some statements to send some messages. So I have written function in javascript and it does what I said, but I saw a problem in javascript code, i used the code javascript: %submitaction_%formname(document.%formname,'RECORD_FIELD')
after this statement i written some code to send the messages.

In the above javascript the record field contains the code to save the component (used DOSAVENOW()).

But I seen the problem like the statements after the %submitaction code are executing before saving the component, they are not in sequence.

Can you please suggest what i have to do to wait until the component is saved and after that i have to execute the next statements?

Please reply your thoughts.
Thanks.
Shiva

Jim Marion said...

@shivakumar, if I understand correctly, you have a mixture of JavaScript and PeopleCode. The submit action code is JavaScript code whereas the DoSaveNow is PeopleCode. Any JavaScript that is going to execute in the context of a page must execute before PeopleCode. Executing PeopleCode from the component processor requires the browser to submit the page. This stops JavaScript processing, keeping any other JavaScript from executing. After the app server finishes processing PeopleCode, it will build a new page and display that page. That new page will have no concept of the previous JavaScript execution state.

If you want to execute JavaScript after DoSaveNow, then use an HTML area bound to a derived work record. When the link is clicked and the PeopleCode DoSaveNow runs, set the value of the derived work record to the JavaScript you want to execute. If your JavaScript interacts with the DOM, then be sure that your JavaScript waits for the DOM to be ready. If you are just calling alert("..."), then you can let those execute immediately.

In Search.... said...

Hi Jim,

I need to do conditional processing in the Java Script. My customer wants to display the message that system is going down whenever the user clicks on any button/navigation link.
The administrator will set up the message on one of the pages and based on the set up for all the users the message should pop up.
We have achieved this for all other procressing except for when the user clicks on Refresh button in the Page Toolbar.

So now my requirement boils down to modifying the PT_PAGESCRIPT HTML while being able check the condition by querying the database.

Could you guide me how can we query the database?

Thanks in advance.
Ekta

Jim Marion said...

@Ekta,

"My customer wants to display the message that system is going down whenever the user clicks on any button/navigation link..." I assume you mean, only when the system is going down, not every time someone clicks a link :).

Generally speaking, to query the database from JavaScript, you make an AJAX request to an IScript. You can find several examples of IScripts in my blog posts.

I think the easiest way to implement the functionality you desire is to override the submitAction_winx functions and/or saveWarning functions by implementing your own version of these function and then calling the original functions after checking the up/down state of the application.

In Search.... said...

Hi Jim,

Thanks for the inputs.
Your solution works perfectly fine when used from an HTML area on a page.
But my requirement is to call iScript from PT_PAGESCRIPT HTML.
Could you throw some light on how can I call iScript from the HTML object?

Thanks,
Ekta

Jim Marion said...

@Ekta, use the approach presented in this post, but for the script source, set it to the URL for an IScript. When the Web Browser encounters the document.write statement, it will insert a script tag and then download from the IScript.

Karthik said...

Hi Jim,

Thanks for your earlier comment on the iSCript.

Is there anyway to access a hidden field on the page using JavaScript?

I basically want to execute the field change of a hidden button using JS. If the field is hidden, it won't appear on the HTML view source and as a result, JS to execute the onclick event for the hidden button doesnt work.

Karthik

Jim Marion said...

@Karthik, only if you check the "interact with JavaScript" checkbox in the page field properties.

VirtualMadman said...

Hi Jim,

I'm using the PT_COPYURL HTML object to add Google Analytics code to our environment. It's working great!

The only problem is that the PeopleSoft URL structure only goes as far as showing the Menu.Component a user has visited, but not the page. I tried using the %Page Meta-HTML variable to log the page name as an custom field in google analytics, but apparently not all Meta-HTML variables work, most likely due to the way branding is rendered using Iscripts.

Here is my question to you: Do you know what App package invokes the PT_COPYURL object? I was planning on inserting the page name by using a bind variable instead.

I would appreciate any feedback if there is an easier way to do this.

Thanks!

Jim Marion said...

@VirtualMadman, that is a very good use case for JavaScript injection. I am pretty sure PT_COPYURL gets into a page through the C++ code in the app server. An alternative is to parse the page ID from the strCurrUrl JavaScript variable.

VirtualMadman said...

Hi Jim,

Thanks for the tip, but it seems like strCurrUrl doesn't consistently display the pageid parameter. I can't confirm this, but it appears that if the component has only one page, the pageid parameter is not shown.

I've found ways to use bind variables, passing them in branding app packages, but these only appear in the template. I'm looking for a way to inject it so that it appears in the content section, so that even if the page is called with the /psc/ url (i.e., through Portal), the page hit would be registered in Google Analytics.

I'll keep digging, but if you know of a way, please share it.

Thanks again for your response.

Jim Marion said...

@VirtualMadman, what you are saying is correct. My thought was for Google Analytics purposes, you just need a unique URL for each page view. If a component has multiple pages, then you can make each page view unique with the page name from strCurrUrl. Otherwise, the URL with just menu/component/market would be enough to identify the unique page view.

You know the scenario where strCurrUrl will not have the PAGE parameter. The only way I know of to identify the page is to make an Ajax request to an iScript and have that iScript look up the page name based on PeopleTools meta-data. I would only do this if I absolutely needed the page name for every single page view and using a default value for single page components isn't enough.

Unknown said...

Jim,

I've found that trying to bind an onClick event to a PeopeTools button using the jQuery's click function causes issues due to the fact that the Tools button makes a javascript call in the href attribute. I found that using the getElementbyID.onClick =(f(x)) solves that problem, although I haven't tested it out on grids yet, but it works on simple pages with only level(0). Have you had any trouble with this in the past?

Jim Marion said...

@Peter, the behavior you are seeing is correct. I would still bind using the jQuery $.click or $.on, but clear the onclick attribute using $.prop("onclick", null). See the "prop" answer on Stack Overflow.

Leandro said...

Jim, I've been following the chapters on your book re. UI enhancements by configuring user scripts linked to a Component/Market/Page/etc., and I've come across a problem.

I'm on PT 8.52, and I added the call to the IScript_UILoader which loads the UI scripts for the current component/page/etc. in a common library called from PT_PAGESCRIPT.

I have a specific UI script that I want to load for a given page in a component, but not for that component's search page. The problem is that when I navigate to the component and land on the search page, the UI loader is executed correctly, but when I search for and open a row of data then the UI loader doesn't get executed again (Update/Display mode is entered to via an AJAX POST). This means that the UI script configured for the specific page within the component is never loaded and executed.

Any ideas?

Jim Marion said...

@Leandro, I experienced the same issue as you as I was putting the finishing touches on this book. I wrote the book on PeopleTools 8.49, and tested it against 8.50. During my 8.50 tests, I discovered the Ajax form post from the search page and came up with the solution posted here. In 8.52, net.ContentLoader was replaced with net2.ContentLoader. By Monkey Patching net(2).ContentLoader, you can trap that form post and load UI Scripts.

Leandro said...

Jim, thanks for the pointer, I think I've got the hang of it now. This has added a considerable level of complexity, though... I've followed your idea of using single-execution flags for the UI scripts (e.g., the "highlight current field" code will run only if apt.ui.highlight is undefined). However, this is no good with PT 8.52, because the AJAX calls reset the styles but leave the single-execution flags untouched. So I have two options: either (a) get rid of all single-execution flags; or (b) reset the single-execution flags in the redefined net2.ContentLoader function prior to reloading the UI scripts.

I understand the underlying purpose of the single-execution flags, but do you think this is something that still applies in PT 8.52, where apparently every action will cause a UI reset?

Thanks as always!

Jim Marion said...

@Leandro, I see what you are saying. It would be OK to remove the execution flags so that it runs each time. There is code that doesn't need to be rerun because it is already parsed and in the JavaScript object model. Running those pieces again won't hurt. It will just redefine what is already there. It is not efficient client side, but from a maintenance perspective, it may be easier than trying to separate the code so that you have the parts that need to run every time and the parts that only need to run once.

truffing said...

Jim,
I'm loving jquery dialog UI at the moment. However, I ran into a snag. With the dialog you can have custom buttons that fire off code. I would like to have an 'OK' button fire off code to update a database table. I'm having problems getting the 'OK' code to talk back with peoplecode to perform the update. I've tried calling an IScript to perform the update, however the underlying page changes when I do the call out to the IScript. I would prefer the page remain untouched once the 'OK' button is clicked. I've tried onclick too in java and haven't had much luck. Any suggestions?

Jim Marion said...

@truffing, regarding the buttons... I'm trying to figure out what you mean when you say the page changes when you click the button. If you are using JavaScript/Ajax (jQuery's $.get, $.post, or $.ajax), then the page and component should not change in any way. It should be completely transparent to PeopleSoft. I've used this approach before and it is very effective. Are you using a different approach to call the iScript?

You can also use JavaScript to execute actions by executing click or by firing the appropriate SubmitAction PeopleSoft JavaScript function.

The only problem I can think of is if your iScript is updating database tables that are used by the component. Doing so may cause a component data inconsistency problem. If your iScript is updating other data, or if the component is read only, then no problem.

truffing said...

I have it working better than I did before. I'm using the following syntax to call the IScript to update a custom database table:
$(this).load('%bind(:1)?MSGID=%bind(:4)'
Bind :1 is a url generated by the generatescriptcontenturl peoplecode command.

My new problem is, the Jquery Dialog is displaying in my prompt table search pages. I don't want it to. It appears that my 'load' of the Iscript is sending a "GET" transaction. If a "POST" transaction was performed the dialog box would not showup. I'm wondering if I should scrap the "load" of the URL and switch to a post/submit.

truffing said...

Jim,

My theory didn't pan out. I still see the jquery dialog box in my prompt table page. I think I have to determine that the page is a prompt table page. If so, don't execute the code. I imagine that means looking for a button named #ICSearch. Any other ideas on how to accomplish this task?

Jim Marion said...

@Truffing, I don't think I have enough information to understand. Do you want a dialog sometimes, but not when there is a search page? What is triggering your code? Is it firing for each page load? Your JavaScript should not fire in a search dialog unless you wrote it to trigger in a search dialog or wrote it to fire for each page load (such as putting it in PT_COMMON or something).

To answer the main question though, yes, you can test for the existence of #ICSearch to determine if you are on a search page. I use the same approach to identify search pages. Here is a jQuery example. Notice that you have to escape the # in the ID:

$("#\\#ICSearch").length > 0

truffing said...

Jim,

I have the code placed in PT_Pagescript which loads IScripts. The IScripts load the stylesheet and the jquery dialog HTML code. The issue I experienced was the job record has a prompt field called Position Number. The prompt page opened up and at the bottom was my jquery dialog box, within the prompt page. The prompt page looked horrible. I turned off the jquery dialog box this morning on search pages by checking the body class for PSSRCHPAGE using JQUERY. I used an if statement like: (!$("body").hasClass("PSSRCHPAGE")). It so far is working perfectly. A lot more testing needs to be done before I'll feel 100% comfortable (secondary pages, sub pages, anything else I can think of.)

Jim Marion said...

@truffing, I get it now. Prompt pages. I thought you meant the component search pages. Yes, your test is good.

When you say you are seeing the jQuery dialog code, I assume you mean you are seeing the dialog's HTML? I usually take one of two approaches:

1. Append the dialog HTML to the body from the event that creates the dialog, and then remove the HTML on close or

2. Add the dialog HTML, but make sure it is hidden with CSS display: none.

Since you don't see the HTML on the normal page, just in the search dialog, I assume you are already doing something like this. But if you have it hidden, it makes you wonder what is making it visible.

Nevertheless, since you don't want it on prompt pages, your approach of checking for prompt pages/search pages is better than letting it exist on all pages.

A note about PT_PAGESCRIPT: it is form specific. If you have a page with multiple forms, then you will have multiple PT_PAGESCRIPTS, each with its own winX number, where X represents a number. These contain form specific functions, such as submitAction_win0, submitAction_win1 (don't ask why it was written that way, and definitely don't ask what I think of it!). Unless you are using %FormName Meta-HTML in your custom code, I recommend that you move it to PT_COMMON (or PT_COPYURL, depending on your tools release). These HTML definitions are only inserted once per page, not once per form, and are cached generically, rather than by form. You may not notice a difference on normal pages, but you will notice a difference on homepages that have lots of component based pagelets.

jb said...

Jim, is it possible to use html5 in PeopleCode to create drag and drop functionality for a field? Can you do this with javascript? I want to move a field on a calendar by drag and drop.

Jim Marion said...

@jb, Yes you can use HTML5 with PeopleSoft, but HTML5 is completely unrelated to PeopleCode. PeopleCode runs on the server, HTML5 runs in the browser. From PeopleTools and PeopleCode, you can generate the necessary HTML5 using iScripts, HTML definitions, etc.

Please note: You don't need HTML5 for drag and drop. You can use jQuery draggable and jQuery droppable to get drag and drop. We have been using drag and drop in our servers for nearly a decade, long before HTML5.

jb said...

Jim, thanks for the info. I am working through chapters 5 and 6 of your book, PT Tips and Techniques to learn more about using iScripts and javascript. Do you know of any specific examples where drag and drop functionality exists in PeopleSoft FSCM? I am not aware of any... Also, do you have any examples of using jQuery draggable and droppable?

Jim Marion said...

@JB, I have used jQuery sortable most recently. I haven't used draggable and droppable for a very long time since sortable meets my needs. jQuery UI is included in PeopleTools 8.53, so you don't have to install it yourself.

sri said...

Hi need to sort a grid column.The Grid column is an html. I wrote the below script to create column heading and enable sorting.But once I click the column header ,it sorts the column and the header disappears .Please help.Please see my code below.

http://www.google.co.in/url?sa=t&rct=j&q=&esrc=s&frm=1&source=web&cd=4&cad=rja&ved=0CEAQFjAD&url=http%3A%2F%2Fjjmpsj.blogspot.com%2F2009%2F01%2Finjecting-javascript-libraries-into.html&ei=-8AlUv-hIsrTrQebvYDgAg&usg=AFQjCNFLaG9PnLe6GjzmFd2dgIisIk113g&bvm=bv.51495398,d.bmk

Jim Marion said...

@Sri, PeopleSoft grids already support sorting.

jb said...

Jim, I have worked through the project in Chapter 5 of your Tips and Techniques book. I was able to display the Flash file when executing the iScript function from the URL. However, when I try to display it on a page in a component, I get the message that my browser does not support Flash.
Any ideas why it would display one way and not the other?

Jim Marion said...

@JB, I have no idea. Are you using SWFObject? Can you look at the downloaded JavaScript and PeopleCode to make sure all the variables, function calls, and HTML look correct? I assume you tested with multiple browsers as well?

VirtualMadman said...

Jim, I may be overlooking this, but it seems like in 8.53, the PT_COPYURL HTML object is gone. is that the case? We used to insert Google Analytics code in this object and now we can't find a suitable replacement. any ideas?

VirtualMadman said...

Hi Jim,

is PT_COPYURL gone in 8.53? I can't seem to find it anymore. We used it for Google Analytics code and now we can't find a suitable alternative. any ideas?

Jim Marion said...

@VirtualMadman, yes, the appropriate place post 8.52 is PT_COMMON. PT_COPYURL was merged into PT_COMMON to reduce network requests and latency.

I am always skeptical of injecting code I can't control into my enterprise applications. For example, Google Analytics offers great reporting, but a consumer has no control over the generated scripts that are inserted into pages. Have you considered an alternative that offers you control over the network and server hosting the analytics application? I know people that use Piwik as an alternative to Google Analytics.

Anonymous said...

Hello Jim & All,

I was looking for location of following javascript code but didn't found in any of the peoplesoft delivered HTML objects. Do someone knows where is it located? It is stored in some message catalog? What is the message catalog number? Is it generated by appending to the string object?

function submitAction_win0(form, name)
{
setupTimeout();
if (!popupObj_win0.updatePromptActn && name.indexOf('#ICQryDownload')==-1) { processing_win0(1,3000); }
preSubmitProcess_win0(form, name);
var spellcheckpos = name.indexOf('$spellcheck');
if ((spellcheckpos != -1) && (name.indexOf('#KEYA5') != -1))
form.ICAction.value = name.substring(0,spellcheckpos);
else
form.ICAction.value=name;
form.ICXPos.value=getScrollX();
form.ICYPos.value=getScrollY();
if (!ptCommonObj.isAJAXReq(name) && !ptCommonObj.isPromptReq(name)){
if (nLastAction == 1 && nResubmit > 0) return;
form.ICResubmit.value=nResubmit;
form.submit();
if (name.indexOf('#ICQryDownload')==-1)
nResubmit++;
}
else if (ptCommonObj.isPromptReq(name))
pAction_win0(form, name, arguments[2]);
else
aAction_win0(form, name);}

Anonymous said...

Hello Jim & All

I'm looking for the location of the following javascript code. I search in all HTML object, however didn't found it. Do somebody knows the location of the javascript code?
Is it stored in message catalog? What is the message catalog number? Is it generated from code, by appending the strings?

function submitAction_win0(form, name)
{
setupTimeout();
if (!popupObj_win0.updatePromptActn && name.indexOf('#ICQryDownload')==-1) { processing_win0(1,3000); }
preSubmitProcess_win0(form, name);
var spellcheckpos = name.indexOf('$spellcheck');
if ((spellcheckpos != -1) && (name.indexOf('#KEYA5') != -1))
form.ICAction.value = name.substring(0,spellcheckpos);
else
form.ICAction.value=name;
form.ICXPos.value=getScrollX();
form.ICYPos.value=getScrollY();
if (!ptCommonObj.isAJAXReq(name) && !ptCommonObj.isPromptReq(name)){
if (nLastAction == 1 && nResubmit > 0) return;
form.ICResubmit.value=nResubmit;
form.submit();
if (name.indexOf('#ICQryDownload')==-1)
nResubmit++;
}
else if (ptCommonObj.isPromptReq(name))
pAction_win0(form, name, arguments[2]);
else
aAction_win0(form, name);}

Unknown said...

We want to use Webtrends (like Google Analytics) with PeopleSoft to capture aggregate page usage and user navigation patterns. We have tried inserting the HTML code into PT_IFRAME_HDR_SWAN but it only executes for the first page after the person logs in. We tried to insert the extracted javascript into PT_COMMON but the constructor is unable execute because the code cannot find the JavaScript file even though it seems accessible. We would like to know where to insert the client side code so it is executed for every page.

Jim Marion said...

@Myron, PT_COMMON is on every page. That is the correct place. I suggest you try to figure out why it can't find the file you need. You might use your browser's developer tools and look at the network and resources information. That usually gives you the full path to the files the browser attempted to access. Then you can compare the path and see why it can't find the file.

Jay said...

Hi Jim , We are having an issue after upgrading PS Tools from 8.50 to 8.52.
We are accessing self service ps pages from a out side portal .

Page is opening correctly,when we click the 1st button it is working fine , but when we press the same button or another button is is just showin the wait icon at the right corner.

this is happening with the buttons/links which is calling submitAction_win0

Jim Marion said...

@Jayanta, I suggest opening your JavaScript console and looking for a JavaScript error. After opening the console you may need to refresh the page to see the error. I'm suspecting there is an error blocking processing from finishing. I also suggest you open a case for this because it may be caused by a known bug fixed in a later tools release.

Jay said...

Thanks for prompt reply.
No error in console except
Failed to load resource: the server responded with a status of 404 (Not Found)
../csspapp/theme/images/banner.gif .
which is coming from the portal side not PS.
Looks like it just waiting for something , any way we can put some debug code the find it ?

Jim Marion said...

@Jayanta, the network tab of Chrome tools or Firebug (or fiddler) will tell you what it is waiting to receive. If it is truly waiting, then I suspect an app server process crashed, and then you should see some type of exception in the app server log.

Kabudles said...

Hi Jim - how do you implement JavaScript in a grid? whenever we add another row of data in a grid we are having trouble locating the specific row of data added?

Do you have any suggestions? Our plan is to have an HTML object that contains the JavaScript function outside the grid and have an onblur function on the grid to trigger the function outside the grid. But is also having a hard time calling the JavaScript function outside the grid.

Jim Marion said...

@Kabudles, you definitely want your HTML area outside of the grid. Otherwise your HTML area will be repeated for each row.

The thing about a grid is that it has no onblur. Each editable field within the grid has an onblur. You will need to attach your behavior to each field. The easiest way to do that is through jQuery or Document query selector

Kabudles said...

Thanks Jim, do we need to modify delivered objects to apply JQuery? Sorry not yet that familiar with JQuery. Can you provide some samples if possible.

Kevin Weaver said...

Jim,

We are considering moving our JavaScript libraries onto our PeopleSoft web server. Do you have an recommendations or best practices on where these files should be stored on the Web Server?

Thanks!

Kevin

Jim Marion said...

@Kevin, here is my recommendation for putting files on the PeopleSoft web server: don't do it! It is a pain in the neck to manage through upgrades and/or regenerating PIA. Here are two alternatives:

1. Internal high availability web server for hosting JavaScript, CSS, and images (like an internal CDN).

2. HTML definitions in app designer. These get copied to the web server on first use so this is the same as your idea, but without the pain of managing them.

A good decade ago I would have put files on the PeopleSoft web server, but that was back when HTML definitions had a size limitation.

Unknown said...

Hi Jim
I am working on branding implementation for our client. We have upgraded our system to PT8.54.13 and PIH 9. We are using some third party javascript and stylesheets for our pagelet content display. We have those third party js and css files in the web server. In the earlier version, we used to include them in the branding header html. But now we got away from Application branding and started implementing the branding which comes along with PeopleTools 8.54. Now my question is how can I insert the third party js and css files into the head tag of the html document.

Thanks in advance,
Muthukumaran M R

Jim Marion said...

@Muthu,

I believe it is much easier in PeopleTools 8.54. First, navigate to PeopleTools > Portal > Branding > Branding Objects. Upload your JavaScript and CSS. Next, Navigate to Branding > System Options. Add any global JavaScripts to the grid and add CSS to the CSS grid. PeopleTools 8.54 takes care of the injection.

Unknown said...

Hi Jim, Thanks for your reply.

Actually it is not a single plugin. Many third party plugins are there in the web server for rendering jQuery based tabs, tree structures, charts, grids, sliders, and lot more. Nearly more than 4500 files and 600 folders are there. I thought of the solution you provided earlier. But because of the large number of files and size, it is not practical for me to upload all of them in Branding objects. Another problem is that these files are in a defined folder structure. If I upload through Branding objects, I can't have the same folder structure.

Jim Marion said...

@Muthu, you are correct, that would be impractical. Using application branding, you would add a new child to your branding header and specify the type as Basic HTML. You can then set the Attribute type to either Application Class, HTML Object, or Static HTML. Either one of these options will allow you to include the necessary markup to describe a JavaScript or CSS file that exists on a web server. This is very similar to the custom HTML items in the PIH templates. The PT version, however, is easier because you don't have to clone and configure a new template.

Unknown said...

@Jim, I have found two options to include the web server files (js, css, images) to the HTML document.
1. The one is as you said, using one of the PT branding attribute types. In fact, this method is powerful because it allows us to add some dynamic elements within html generated with the help of application class peoplecode. For e.g., If I want to generate a web server file path with respect to the instance name dynamically, then I can use application class type in Basic HTML attribute type like the below one.

<script src="/instance_name/js/utils/test.js"></script>

I can have the instance_name to be variable for different instances. But one thing to be noted in this method is, whatever files we include here goes inside body tag instead of head tag of html.

2. I can edit the html object (PORTAL_HP_USER_TEMPLATE) in application designer which renders the entire homepage and PT_HNAV_TEMPLATE for the transaction pages. Here I can place the <script> tags and <link> tags inside head tag. But I am not sure about if there is any harm or disadvantage in editing these delivered HTMLs.

Jim Marion said...

@Muthu, I wondered about the placement of the scripts, etc. Usually placing scripts and CSS inside the body instead of the head is not a problem.

As far as changing delivered definitions. The only problem is upgrades, patches, bundles. You will need to manage your changes through the patching process.

Unknown said...

You are right Jim. Thank you for your valuable suggestions. I think I can go with any one of the above options.

Unknown said...

Hi Jim, Another issue I am facing is with pagelet rendering. In our client's PeopleSoft Portal, some developers have created custom pagelet builder feature sometime back. This is somewhat similar to the delivered Pagelet Wizard feature. Using this custom builder, they are creating pagelets with it contents in folder tree structure, Tab based layouts, etc.. They have used some third party plugin called Zapatec to bring all these UI features. It was working fine till the last PT release (8.53). But in an upgraded (to PT8.54) instance, none of the UI (tabs, folder tree structures) are working. I mean tab layout is not displayed, only the texts are displayed instead. I have used JS console and other debugging tools in order to troubleshoot the issue, but could not find anything.

I am comparing the rendered HTML between the 8.53 system (COP) and upgraded dev instance (8.54) and found that exactly same HTML markup is genereated for the tab layout section. Even corresponding styles are also loading in the <head> tag. But the only difference is that if I inspect the tab display element (for e.g., <div class="innerTab">) using F12 key, the corresponding style properties are displayed on the right side of the inpect element window as usual. And the source of this css lies inside the <style> under <head>.

But if I inspect the same element in 8.54 upgraded instance, the appropriate style properties are not appearing on the right side panel of the inspect window.

In other words, these tab HTMLs are generated properly and the required styles are also available in the <head> tag. But these styles are not applied to the tab div tags for some reason. I don't understand why these are not applied. I wonder if you could provide me some suggestion for this issue.

Jim Marion said...

If the style definitions are in an external file, then check the browser network tab to see if the browser is able to find the file. If it is embedded CSS, then the problem is likely with the selectors. Then you needed to confirm that the CSS selectors still match. The selectors may contain more than just a class name.

Shiva said...

Hi Jim,

I have a requirement where I need to disable the grid with a single selector when a logged in user is not a proper user.

In peoplesoft there is no option to disable the grid row selector, so what I did is I put an HTML area on the page and written the javascript to disable the grid row selector based on the CSS class associated to it for that field ID.
I loaded this script on the page based on the condition on page activate event.

And here the problem is the selector is not disabled for the first time when the component is loaded but if I move back to different pages the code is working.

Am I missing something here, Please provide your comments.

Thanks,
Shiva.

Jim Marion said...

@Shiva, I'm suspecting this has something to do with the onload/DOM ready event handler. PT 8.5x uses Ajax, so it is very difficult to identify the page load. I would put some console.log statements in the JavaScript to see if your JavaScript is triggered.

Now here is something to think about from a security perspective: can someone open the console window and renable the row selection to bypass your security mechanism?

Srinivas said...

Hello Jim,

Thanks for sharing the article!

On a fluid page, I'm injecting javascript into HTML area using some javascript libraries like chartjs, jQuery, and Oracle Jet. HTML area displays charts and PeopleSoft flex-grid, page works fine when it loads for the first time everything renders perfectly.

Issue:
There is fieldchange event on the grid (level 1) to open an URL, when I click this hyperlink, page refreshes, hyperlink works as expected, HTML area is gone, JavaScript doesn't load this time. Do you happen to come across the similar issue?

Troubleshooting:

1. I experimented with deferred/interactive processing modes (on record field, page and component) by flipping flags, the whole page still refreshes --Didn't work
2. Removed tags from HTML/javascript those are not supported by PeopleSoft HTML definitions --Didn't work
3. Inspected element on the page to see if there is any code behind the browser, there javascript code when page loaded initially, it disappeared after hyperlink click --Didn't work

Srinivas said...
This comment has been removed by a blog administrator.
Jim Marion said...

@None, what you are seeing is very common. With every PeopleSoft Ajax update the full page is rerendered, wiping out any JavaScript changes you have made to the page. Unfortunately, there is no delivered event for this. The way I handle this is to "MonkeyPatch" net2.ContentLoader. Then I can see each Ajax request, and then reapply my changes after update. You will find several examples of this in my blog, with the most recent being the post about click-enabling the buttons at the bottom of a Fluid homepage.