This blog is mainly for notes to myself, mostly about programming. However, there may be others at CUNY facing similar programming problems. For that reason, and because this site is public, it’s a little more “composed” than my normal notes. Programming has changed a lot since I started back in 1976. Nowadays, first thing I do when I have a problem is search the web to see if someone else has already solved it. My code is often mostly “glue,” it glues together pieces (subroutines, libraries and the like) from disparate sources.
This post is about client-side mashups. It discusses techniques for combining information from various external web sites into a “target” page. In particular, a target that you don’t wholly “own.” A page, say, provided by vendor software running in an externally hosted environment. In this case, PeopleSoft at CedarCrestone. John Donovan is my local PeopleSoft expert, he built the target application that links to the “masher” page that Ross Fried and I wrote. The masher collects information from a couple of external web services and injects it into the target.
First off, we had to deal with the browser enforced “same origin policy.” That means the masher page (served out by masher.cuny.edu at 57th Street) had to be in the same domain as the target (target.cunyfirst.cuny.edu at CedarCrestone). All CUNYfirst (our PeopleSoft implementation) hosts have names like xxxx.cunyfirst.cuny.edu. So Cedar created the CNAME masher.cunyfirst.cuny.edu, a DNS alias, for masher.cuny.edu. All CUNYfirst pages set their domain to cunyfirst.cuny.edu. So the masher must
document.domain = 'cunyfirst.cuny.edu';
If the masher and target are not in the same domain, you’ll see something like this in your browser’s error console when the masher tries to access the target:
Error: Permission denied to access property 'document'
You’ll see the same error if a non-secure page (HTTP/non-SSL) tries to inspect a secure one (HTTPS/SSL). All CUNYfirst pages are delivered by SSL, so the masher has to be opened via HTTPS. John put a PeopleTools “HTML area” in the target page which opens the masher like this:
function injectHere(element) { var targetDetail = element.parentNode.parentNode.id; // Find detail index, up two levels var masherURL = "https://masher.cunyfirst.cuny.edu/mashup/masher.html?" + targetDetail; var masherWindow = open(masherURL,'masherWin','scrollbars=1,toolbar=yes,menubar=yes,resizable=no,width=600,height=500'); if (masherWindow.opener == null) { masherWindow.opener = self; } return false; }
PeopleSoft imposes a layer of abstraction between the developer and the web. Getting it to do what you want is a bit of a wrestling match. Hence, the first two lines in injectHere. They tell the masher which field to update in the target. The target consists of a PeopleSoft “scroll area” containing several “details”. Each detail has an associated index (the number after the “$” in the HTML tag’s ID). John added a button (another “HTML area”) to each detail that calls injectHere(this). When called, injectHere uses “this” to find the id of the target detail and adds it to the URL used to open masher.
<div id="DETAIL$0"> <div> <!-- Begin HTML Area. The masher must go up two levels to find which DETAIL to update --> <input id="targetButton" value="inject" onclick="injectHere(this);" type="button"> <!-- End HTML Area --> </div> <div> <input id="FIRST_FIELD$0" type="text"> <br> <input id="SECOND_FIELD$0" type="text"> <br> <div id="DETAIL$1"> <div> <!-- Begin HTML Area $ICField$1 --> <input id="targetButton" value="inject" onclick="injectHere(this);" type="button"> <!-- End HTML Area --> </div> </div> <input id="FIRST_FIELD$1" type="text"> <br> <input id="SECOND_FIELD$1" type="text"> <br> <div id="DETAIL$2"> <div> <!-- Begin HTML Area $ICField$2 --> <input id="targetButton" value="inject" onclick="injectHere(this);" type="button"> <!-- End HTML Area --> </div> </div> <input id="FIRST_FIELD$2" type="text"> <br> <input id="SECOND_FIELD$2" type="text">
The masher’s main job is to inject information drawn from several external WebServices back into the target (the page that opened it). First masher determines the index of the target field by parsing the URL that it was opened with.
inject
function inject() { var href = window.location.href; // URL we were opened with // The target's index is after the "$" at the end of our query string var lastNum = /.+\$([0-9]+)$/.exec(href); if (lastNum == null) { alert("No index passed to search page: " + href); return null; } var targetId = "FIRST_FIELD$" + lastNum[1]; var target = opener.document.getElementById(targetId); if (target == undefined || target == null) { alert("Unable to set targetId"); return; } var item = getWebServiceResult(); target.value = item.value; }
In my next post I’ll discuss what getWebServiceResult does.
Thanks for the compliments, John. Like I said, these days programming is usually a collaborative process. Though sometimes that collaboration is virtual, remote or indirect. My good luck, in this case, I got to work directly with a couple of really good programmers. Although this particular “collaboration” of javascript and PeopleCode was tricky. Your note gives me the opportunity to make it clear that my blog’s representation of a PeopleSoft page (the “target”) is a simplification.
What you and Ross did to make this work in PeopleSoft was nothing short of genius. This is exactly the kind of process that makes an exciting and well-attended presentation at any HEUG conference. The techies who attend the HEUG conference are craving for this kind of information. I think we should present it together. Unfortunately, next year’s conference city is Indianapolis. Oh well…