Monday, August 31, 2009

JavaScript complement of PeopleCode Global Vars

While writing some JavaScript for my new PeopleTools book, I came up with a JavaScript complement to some of the common PeopleCode global variables. Here is the code:

jjmpsj.sysVars = (function(url) {
var matches = url.match(
/ps[pc]\/(.+?)(?:_\d)*?\/(.+?)\/(.+?)\/c\/(.+?)\.(.+?)\.(.+?)$/);
return {
getSite: function() {
return matches[1];
},
getPortal: function() {
return matches[2];
},
getNode: function() {
return matches[3];
},
getMenu: function() {
return matches[4];
},
getComponent: function() {
return matches[5];
},
getMarket: function() {
return matches[6];
}
}
})(window.location.pathname);

Now, wouldn't you just love to know how I intend to use this code... to learn that you will have to wait for the book.

38 comments:

Paul said...

Off the top of my head, this looks to be a great way to make the info from those variables available in iScripts. Is that close?

Jim Marion said...

@Paul, IScripts are PeopleCode, so they already have access to all the global system variables. The code in this post is JavaScript and executes in the client side web browser. For example, let's say you place an HTML area on a page. In that HTML Area, you add HTML for a link to open a PeopleSoft Query. To open that query, you need to generate a URL. PeopleCode has a GenerateQueryContentURL function you could use, but that would require a derived work record, etc. Using the sysVars object shown in this post, you could generate the query's URL in JavaScript. For example:

var url = "/psc/" + jjm.sysVars.getSite() + "/" + jjm.sysVars.getPortal() + "/" + jjm.sysVars.getNode() + "/q/?ICAction=ICQryNameURL=PUBLIC.MY_QUERY"

A more common use case is to generate a URL to call an IScript with AJAX.

Paul said...

Hi Jim,
I must be thinking of something else - you are not referring to variables like %component? We are unable to use %component, %page, etc. in our iScripts. As a work around I've either been passing the information in as a parameter or stripping it from the URL.

Jim Marion said...

@Paul, ah, you are right. I was referring more to %Node and %Portal, which are available to IScripts. %Component, %Page, etc are not available to IScripts because IScripts are not part of a component. They don't have a component buffer. Basically, %component, %page, and %menu are out of context with IScripts.

Actually, I'm using the JavaScript in this post to pass menu, component, and market to an IScript, which is exactly what you are doing. The approach presented here allows you to use static HTML rather than having to use a PeopleCode event and HTML definition bind variables to insert component variables. Now that I look back at your comment, I think this is exactly what you were saying. If that is the case, then, "Yes, this is 'a great way to make the info from those variables available in iScripts.'" I apologize for the confusion.

Karam said...

Jim , i am facing an issue with the HELP link,we need to customize some Predefined URL based on the component.Not specific to Page i.e delivered.I was trying to figure how exactlly the help works and where the codes needs to altered.
I understand that this concept is called as context reference,but i didnt get anything musch helpfull.
Could please help me.
Thanks in advace.

Amit

Jim Marion said...

@Amit,

I have no experience with the help system. You might want to post your question in the PeopleSoft OTN general forum.

Paul said...

Hi Jim, It's been a while since this post but I've recently been working on a project that reminded me of your sysvars code snippet. I'm hoping you could give a tip or two on how to supplement the variables you're recreating in javascript.
In my current project I'm trying to pass the page name from javascript to an iScript and I've been as yet unable to find a reliable method. Usually when I'm dealing with javascript on a ps page, I'm able to supply the data from peoplecode - in this case everything is being done from within javascript. I've found a few different ways, but none are generic enough to work everywhere - for instance sometimes the ps javascript variable strCurUrl contains the PAGE parameter. Do you know of a way to get the page name within java script?
On a related note - while looking at the page source, I noticed at the very top there is a comment section that has all the sys variable info - workstation; ToolsRel; Page; Component; Menu; User; DB; AppServ - is there anyway to strip that from the source to use in a bit of javascript?
thanks for your help,
Paul

Jim Marion said...

@Paul, strCurrUrl is the only way I know of to get the page name. You are correct. Sometimes it isn't there. I believe the page name will always be there UNLESS you are using the default page for the component. This means that if there is no page name on the URL, then the page name can be derived from some SQL against the PeopleTools PSPNL% record definitions.

I've seen the comments too, but do not know of a way to parse them. I've considered it because all the info is there, but have always found other ways to get the required information.

Paul said...

I found a rather clumsy way of parsing the comments just a few minutes ago:
you can grab the source via a get
$.get(location.pathname,function(data){alert(data);});
and then pick through the returned data and pull out the page name, component name, etc. I'm sure this could be cleaned up and made more efficient, but it very well might be easier to do as you outlined and use strCurUrl.. thanks Jim!

Paul

Jim Marion said...

@Paul, Right. Making a second http request would certainly do it, but the performance, etc, wouldn't be good. Likewise, it may confuse the component processor/state with that unexpected HTTP request. It seems like you could get the content of the document (including comments) from the DOM without making an extra http request.

Paul said...

Ah, of course... document.all[0].innerHTML.match('Page=(.+?);') - much simpler than a whole other get.
thanks for pointing me in the right direction!

Jim Marion said...

@Paul, right. If you are a jQuery user, like me, then you can use something like $(document.documentElement).html() or $("html").html(). If you aren't using jQuery, then you can try these DOM traversals as well: document.documentElement.innerHTML or document.getElementsByTagName("html")[0].innerHTML.

I wish there was a good way to target that specific comment node rather than running a regular expression against the entire document contents, but I haven't figured it how to do that with the DOM. You can iterate over DOM nodes and find comment nodes, but I'm not sure that is any better.

For your regular expression, I suggest using the "word" character class (\w) instead of matching all characters with the period (.). I also suggest adding the comment separator to your regular expression to make sure your match only selects comments. Of course, since the comment is at the top, if you only use the first element in the array, you are likely to get the right result even without narrowing the regular expression. Here is an example:

var page = document.documentElement.innerHTML.match(/<!--.+?Page=(\w+);/)[1]

Paul said...

I do use jquery, and thanks for the $("html").html() I'll be using it for brevity's sake. I'm curious about your regex, though. I tried using the "opentag!--" but it wreaks all sorts of havoc with pages in IE6 (of course, everything works fine in other browsers..). Did you not experience any issues with that?

Just as an explanation for all these questions - the thing I'm working on is a small tool tip for developers and SMEs in our shop. It shows the record name, field name, prompt table, and what events have code (and who updated them last and when) for the field you're hovering over. The page name was important because some fields aren't represented by record_field in the html, but only by field. the page name lets you pull the record name off pspnlfield.

Jim Marion said...

@Paul. Brilliant use of the tools! I did not try my regular expression on IE, only Firefox. I apologize. Thank you for pointing that out.

Paul said...

hah, I hadn't either - until 4 people rushed my cube and asked why I broke our development environment!

Thanks for everything. It's been immensely helpful.

Paul said...

Jim, I just had a chance to look at the portion of your book pertaining to this code - and wow! We use it in slightly different ways, though not for long (we'll be switching soon enough!) Your book is fantastic and incredibly helpful.

There is one thing I found in using the sysvars complements that I'd like to run by you. As published, the regex doesn't pick up the worklist or the main menu pages. I found this slightly modified regex will also include those two locations -
ps[pc]\/(.+?)(?:_\d)*?\/(.+?)\/(.+?)\/[chw]\/(?:(.+?)(?:\.(.+?)\.(.+?))?)?$
Is there a reason I'm not seeing as to why we shouldn't include these variables in those locations?

also, i appologize for continuing to comment in your older posts - I didn't think this would have made too much sense in your later posts.

Jim Marion said...

@Paul, don't worry about commenting on older posts. I don't mind at all. I am really pleased that you find the book useful.

About worklists and menu pages... I actually hadn't considered them. Thank you for adding to the list of variables.

One more thing, in an earlier comment I said I hadn't tried my RegEx on IE. I don't know why I said that. Yes, I have tested it on IE, and I'm not sure why it didn't work for you.

DanB said...

@Paul, I don't see the variables on top of html page when viewing source from Peoplesoft. Are they hidden? I did take yours and Jim code to pull the page name in Javascript and it appears to work. Just would like to see what I'm pulling it from. :)

@Jim, by the way, I bought your book and just started to scratch the surface but so far its very good. Finally a good book that goes outside the peoplebooks material!

Jim Marion said...

@DanB, when viewing the source, be sure you are viewing the source of the right document. In IE, I think it only gives you the option of the document you selected. So, if you click on the header, you get the outer portal part. If you click in the transaction page, you get the component. In firefox and other browsers, you can choose to view source, or This Frame > View frame source. Either way, make sure you are viewing the source of the TargetContent frame.

I'm glad you find the book useful. Yes, my point was to say something no one has said before. I didn't want to just restate PeopleBooks. I wanted something different.

BShreyas said...

Thanks Jim

This is regarding the question I asked in your blog:What is an IScript?. I wished to know how I get the reference of the page, Menu and Component in iScript. I came to know about the constructs available to acess the innerHtml to fetch these things from page HTML top comments. But please guide me how to get them in iscript in my specific case. If snippets are provided I will be grateful as I have just started in this area.

1. I am modifying PT_COPYURL. I have created an iScript which I am invoking through function generateScriptContentUrl.
2. The iscript in turn calls an HTML which will contain the logic to fetch the page, menu name.
3. How shall we pass this menu name to the iscript which is executing the getHTML? If not this iscript then in what way we can capture the menu ,page names etc. I need to do some DB operations with the captured data and then again incorporate them in another HTML.
4. Another question: I tried to use %request.getparameter("JOB_EFFDT$0") hardcoded in the iscript and tried to pass the value to HTML through bind and then alert messagebox. But alert message was empty. The page which I opened had effdt field with the same ID , with non null value. Then why getparameter was not executed. I saw the example in your book concerning the page with menu name, market name etc and getting the values through %request.getparameter("mk"). But was it the case that your code got executed in the contest of component processor hence iscript fetched parameters.

I have certain other questions as well. But I will be grateful if I get detailed reply with code snippets, as I am totally novice in this area.

Thanks in advance..

Jim Marion said...

@BShreyas, the code in my book uses the code in this post to determine the menu, component, and market. It then sends them as parameters to the iScript. That is how the iScript knows the menu, component, and market. The iScript has no component context. The only reason it knows is because the URL I used to access the iScript had these values as parameters to the iScript.

KP said...

Hi Jim,i am working as a developer and even though i know little bit of peoplecode here and there i never really understood how the javascript that really builds the page (?) and peoplecode work together. When i do view source all i see is HTML,Java script etc. I don't know much of Java or Java script. If you can point me to the right direction:a book,article or anything that can help me atleast give a brief idea i would appreciate.

Jim Marion said...

@KP, The JavaScript handles the user interaction, but it doesn't actually build the page. When you see JavaScript embedded in the actual HTML, that usually comes from one of the various HTML templates used by the page assembler to construct the page. The JavaScripts linked in through a script tag usually come from HTML definitions in app designer as well, but these are purely JavaScript files with no HTML (plus some Meta-HTML sequences).

PeopleCode is always for server side logic. It is mostly used to change the state of page fields. You can use it with Derived/Work records to insert HTML/JavaScript into downloaded pages.

Basically, there is no direct connection between PeopleCode and JavaScript. The client side JavaScript handles button clicks, form submissions -- user interaction type of stuff, but not business logic. The business logic is in the PeopleCode on the server. The two are separated from each other by the component processor. There are ways for the two to interact with each other. For example, you can use Ajax in JavaScript to call iScripts, which are PeopleCode functions executed through a URL. As I previously mentioned, you can also use PeopleCode to insert JavaScript. So, the two can interact with each other, but they are really two different languages operating on two different tiers, with very little in common. Neither of them really construct the page. Both have some influence on the final page, but the actual page building process happens in some C++ code on the App Server (something we call the page assembler).

I wish I could point you at something that would give a better explanation, but I don't know of anything. I think you are asking the right questions though. I remember the first time I saw PeopleSoft, I started digging through the web server for a folder named EMPLOYEE with sub directories of EMPL, HRMS, FSCM and then sub directories like c, h, and s. No, these don't exist, so don't look for them.

I doubt this answered your question fully, but hopefully it leads you to other questions. Please, feel free to post again with more questions so we can build on this dialog. Perhaps our dialog can become a sort of documentation for others asking the same question.

tkx said...

Hi Jim,

I am using an HTML area on a page. The page also has a derived field. Now, I want to print the value of this field in the HTML area, but need to keep it invisible on the page. Have tried the Modifiable by Javascript property, but it's not working.

Any help will be appreciated!

Jim Marion said...

@tkx, The way I would do this is to set the HTML Area's source to a derived/work field, and then use a PeopleCode event (such as RowInit) to set the HTML Area's field value. For your Derived/Work field, you can create your own work record and add one of the several HTMLAREA fields to that record.

tkx said...

Thanks Jim! Got it working. I have another problem. There's a component with multiple pages. It's required to implement an activity guide (an Activity definition in App Designer with Activiy Guide property set) to give the user a step-by-step walkthrough of the pages. Am using Ptools 8.51. Have created the Activity definition, added the Activity Guide subpages. Written the following code on the PreBuild event of a dummy component:
&AGURL = GenerateActGuideContentURL(%Portal, %Node, MenuName.CSX_FA_APPL, "GBL", Component.Comp_name, "Page_name");
ViewURL(&AGURL);
What else do I need to do? the Peoplebooks only mention these steps, but the activity guide is behaving very abnormally.

Jim Marion said...

@tkx, Unfortunately, I have not worked with activity guides.

tkx said...

No problem Jim. Thanks anyways. Love your blog; you're doing a great job. Keep it up! :)

Khris said...

Hi Jim,

I have a scenario where I need to re-initialize a global variable to blank after clicking the Home link in the PeopleSoft header or after navigating away from the page I'm currently in. Do you know how to do that? Thank you in advance.

Regards,
Aram

Jim Marion said...

@Khris, to reset a global variable, you need to execute some PeopleCode. Any PeopleCode will do. I see a few options:

#1 Add an iScript or component based pagelet to the homepage. Make it something valuable and then tack on your reset code (sort of like legislation before the senate ;)). This does not involve any modifications.

#2 Modify the iScript that is called when constructing the homepage. I don't recall the name off the top of my head

#3 Modify the default branding package PeopleCode. You can find the branding package by looking in PeopleTools > Utilities > PeopleTools Options. It is something like PT_BRANDING:BrandingBase

#4 Similar to #3, but clone PT_BRANDING:BrandingBase and add your reset code. Update PeopleTools options to point at your package instead.

With the branding options you will want to test something (perhaps the request URL?) to make sure you are on a homepage and not on the original component.

GSG said...

Hi Jim,

I have the following case. The user is redirected from the homepage to a workcenter on signon. I achieved this by following your advice in one of the OTN discussion forum's posting. First I created a new HTML object and added a few JavaScript lines like window.location = "%Bind(:1)", then I modified PT_BRANDING.BrandingBase.GetHPTabHTML to inject the JavaScript like &href = GenerateComponentPortalURL(...) and %Response.Write(GetHTMLText(HTML.MY_HTML, &href)).
This works, but the requirenment is that the user should still have access to the homepage by clicking the Home link in the header. But the link Home executes the same code and takes the user back to the workcenter. Is there a way to find out that the request comes from a component within the portal. I am trying to solve this by running %Request.GetHeader("Referer") and analysing the referer string, but I am not sure it's reliable. The string might differ depending on the breowser.

I am new to Javascript and Portal administration, and I am not sure this is correct thread but I'd be grateful if you could help or give me some pointers.

Thanks,
George

santosh naik said...

HI Jim,

I have code will return me a xml in browser how can i get the xml in variable. when i execute the url i can get that xml. BUt when i execute the url i am getting response in browser. I need it in variable. I am using Javascrpit to execute the url by %response.write method odf peolecode




how can i retrive out put from this to a variable

Jim Marion said...

@Santosh, do you mean a JavaScript variable? Are you asking how to get the XML into a JavaScript variable? Have you considered using jQuery's $.get?

Divya Thottathil said...

Hi Jim,



We have upgraded from 8.48 to 8.52.We had customized PT_PAGESCRIPT in 8.48 inorder to catch a message box and evaluate it and display a pane.But by putting the same customization, it is not working in 8.52.The message box pops up but in 8.48 it does not.We also found that window.alert is no more supported in 8.52.Is there a alternative for this?Here is the customized code in PT_PAGESCRIPT



if (window.standardAlert === undefined) {
var standardAlert = window.alert;
var DIRECTIVE_LENGTH = 6;
window.alert = function(alertText) {
var directiveText;
if (typeof alertText == "string") {
directiveText = alertText.substr(0, DIRECTIVE_LENGTH);
}
if (directiveText == "\\ad66~") {
var scriptString = alertText.substring(DIRECTIVE_LENGTH, alertText.length - 7);
scriptString = scriptString.replace(/%/g, "%");
scriptString = unescape(scriptString);
eval(scriptString);
} else {
standardAlert(alertText);
}
};
}


This is the code in the app package which is called during page load.



method run
Local string &scriptText = %This.buildLibraryContent() | &content;

Local string &dopedMessage = "\ad66~" | EncodeURL(&scriptText);

MessageBox(0, "", 0, 0, &dopedMessage);
end-method;



Do you have any idea why this is not working in 8.52?Also when this custom code is put search buttons and prompts does not work.



Regards,
Divya

Jim Marion said...

@Divya, window.alert is a browser/JavaScript construct. There is nothing PeopleTools can do to implement or limit it. Yes, it still works. I use JavaScript in FieldChange, RowInit, and page Activate events on 8.52. Are you using a derived/work field with an HTML area to add your JavaScript to a page? I suggest you use Firebug to review the contents of your page to see if the downloaded and inserted JavaScript appears the way it should appear.

The key difference between 8.48 and 8.50+ is that 8.50+ pages load through Ajax, so the DOM ready and window load events have already passed when the scripts execute. The PeopleSoft Ajax libraries search downloaded Ajax content for script tags and execute them, so anything added to a page should execute.

Divya Thottathil said...

Hi Jim,

We did function F12 and did a debug to the PT_PAGESCRIPT.In 8.48 it is entering into window.alert = function(alertText) { but in 8.52 it is not.

Also we did a view source in both and found the following

8.48
Alert is executed

setEventHandlers_win0('ICFirstAnchor_win0', 'ICLastAnchor_win0', false);
processing_win0(0,3000);
setKeyEventHandler_win0();
nResubmit=0;
alert('\\ad66~%20window.layout1%20=%20new%20Layout();

8.52
Addmsg is getting executed


if (typeof oWin == 'undefined') setEventHandlers_win0('ICFirstAnchor_win0', 'ICLastAnchor_win0', false);
else
oWin.setEventHandlers_win0('ICFirstAnchor_win0', 'ICLastAnchor_win0', false);
processing_win0(0,3000);addMsg("div id=alertmsg style='overflow: auto; max-height: 300px;'span class='popupText' br \ad66~%20window.layout5%20=%20new%20Layout();html tags here");

Could you please let us know your inputs?

Regards,
Divya

Divya Thottathil said...

I have written a code to parse xml document and put it into tables in peoplesoft.But when I used the below code,some xmls got processed successfully adding them to a table but all of a sudden the process went to error for one of the xmls saying that "XML parser error CreateXmlDoc Fatal Error: at file line: 0 column: 0 message: An excep tion occurred! Type:RuntimeExc eption, Message:The primary do cument entity could not be ope ned. Id=D:\Career_builder\Zip\ CAR_BLD_10\HudsonApps_07022013 _083821.xml (159,5) HH_CAR_BLD"

I am able to open the document without any errors.Could you let me know how this can be resolved?

Code:

&inXMLDoc = CreateXmlDoc();
&ret = &inXMLDoc.ParseXmlFromURL(&filename_to_process);

Jim Marion said...

@Divya, you may want to post this question on the OTN PeopleSoft General Discussion forum. You might also want to open a case with MyOracle Support.