Bug Blog 4: Fix Broken File Downloads in 10gR3 (Part Deux)

Last week I provided portal filter code to fix some elements of opening documents in WebCenter Interaction’s Knowledge Directory (and Search and Snapshot Queries).  This week is another follow-up on that theme, and I’ll provide another piece of code to continue cleaning up the mess that is the Knowledge Directory Downloading Clusterf*ck (in IE at least).

Ever gotten this little present after doing an upgrade to 10gR3, when trying to open a document in the Knowledge Directory using IE7 or IE8?

 

Ah, the old “To help protect your security, Internet Explorer blocked this site from downloading files to your computer.”  What’s even more ridiculous, if you “accept” the download, it STILL doesn’t work because IE tries to reload the page, and WCI is very stupid about how it opens this popup window (see how that address bar shows “about:blank”?).  It seems this only happens in IE, and only if you have adaptive layouts disabled with “open documents in new window” enabled, so it doesn’t affect everyone.  But I die a little on the inside every time I see that stupid thing: yes, my friends, I am an empty, hollow shell of a consultant.

Fortunately, I decided to seek redemption for WCI and myself by proxy, and checked out the HTML source for a typical file open link.  It’s kinda hilarious:


<a href="#" onclick="var currentWin = PTCommonOpener.openInNewWindow('', 'Opener_18_1322446', 800, 600, true); currentWin.location= 'http://server/portal/server.pt/document/1322446/slide_1'; return false;">Doc Name</a>

Whhaaaaaaa…!?  Sure I know there are dozens of ways you can open a document in a popup window, and many have their advantages (for example, by using JavaScript you can control the size and layout of the window), but I’d be hard-pressed to come up with a worse way to open a document.  I’ve always hated the fact that you can’t just hover over the link to see the URL, and can’t right-click and copy the shortcut to mail to someone later. (Tip: use adaptive layouts and these problems go away!).  Or they could have at least just done a window.open method in that onclick event.  OK, so they wanted to use a wrapper method – that’s fine – but the PTCommonOpener.openInNewWindow actually takes a URL paramater, so they could have just passed the URL into that function, rather than creating a new, EMPTY document and then trying to use Javascript to load the document into that window.  At very least they could have put the document URL in the “href” parameter so it’d work without JavaScript (and be 508-compliant) and users could copy the links with a simple right-click.

OK enough rambling.  On to a solution:

It’s clear looking at the above code that while Microsoft’s security controls can sometimes be oppressive, this blocked transfer kinda makes sense:  IE blocks downloads that weren’t initiated by the user (i.e., were initiated by JavaScript).  The problem is, given the brain-dead way that documents are opened in a new window (by creating an EMPTY window first, then trying to download the document later), when you accept the download, the browser reloads the about:blank() page instead of the actual file.

Using a .NET decompiler (I used dis# because I needed to search the entire portaluiinfrastructure.dll for the text string “openInNewWindow”), I found the code in com.plumtree.portaluiinfrastructure.opener.PTOpenerLinks:


public static string GetJSOpenInNewWindow(string _strURL, string _strWindowName, string _strWindowWidth, string _strWindowHeight, bool _bIsWindowFancy)
        {
            com.plumtree.openfoundation.util.XPStringBuilder sbOpenerJS = new com.plumtree.openfoundation.util.XPStringBuilder(300);
            sbOpenerJS.Append("var currentWin = ");
            sbOpenerJS.Append("PTCommonOpener.openInNewWindow");
            sbOpenerJS.Append("('', '");
            sbOpenerJS.Append(_strWindowName).Append("', ");
            sbOpenerJS.Append().Append(", ");
            sbOpenerJS.Append(_strWindowHeight).Append(", ");
            sbOpenerJS.Append(_bIsWindowFancy);_strWindowWidth
            sbOpenerJS.Append(");");
            sbOpenerJS.Append("currentWin.location=").Append(_strURL);
            return sbOpenerJS.ToString();
        }

Ideally, I’d change the code to something like this:


public static string GetJSOpenInNewWindow(string _strURL, string _strWindowName, string _strWindowWidth, string _strWindowHeight, bool _bIsWindowFancy)
        {
            com.plumtree.openfoundation.util.XPStringBuilder sbOpenerJS = new com.plumtree.openfoundation.util.XPStringBuilder(300);
            sbOpenerJS.Append("var currentWin = ");
            sbOpenerJS.Append("PTCommonOpener.openInNewWindow");
            sbOpenerJS.Append("('");
            sbOpenerJS.Append(_strURL);
            sbOpenerJS.Append("', '");
            sbOpenerJS.Append(_strWindowName).Append("', ");
            sbOpenerJS.Append().Append(", ");
            sbOpenerJS.Append(_strWindowHeight).Append(", ");
            sbOpenerJS.Append(_bIsWindowFancy);_strWindowWidth
            sbOpenerJS.Append(");");
            return sbOpenerJS.ToString();
        }

(or even better, figure out how to change this to a normal link like <a href=”http://url” target=_new>docname</a>)

… but recompiling decompiled code – especially in a .NET library as large as portaluiinfrastructure – well, that just wasn’t going to happen.

So we can’t change the HTML that generates this terrible link, but is there at least a way to fix the problem where we can get the browser to open the link in a new window in one step instead of two?  Fortunately, the answer is yes.  I’ll leave the analysis of this hack to you, but basically it creates a global object and allows both of those calls (open the window and set the URL) to be made before actually calling the open window method.

Open the PTHOME\imageserver\plumtree\portal\private\js\ptcommonopener.js file and change:


PTCommonOpener.openInNewWindow = function(_strURL, _strWindowName, _strWidth, _strHeight, _bIsFancyWindow)
{
 return PTCommonOpener.JSUTIL_VERSIONOBJ.PTWindowUtil.openWindow(_strURL, _strWindowName, _strHeight, _strWidth, _bIsFancyWindow);
}

… to:


var newwin = {location:'', _strWindowName:'', _strWidth:'', _strHeight:'', _bIsFancyWindow:''};
PTCommonOpener.handlePopup = function() {
 PTCommonOpener.JSUTIL_VERSIONOBJ.PTWindowUtil.openWindow(newwin.location, newwin._strWindowName, newwin._strHeight, newwin._strWidth, newwin._bIsFancyWindow);
}

PTCommonOpener.openInNewWindow = function(_strURL, _strWindowName, _strWidth, _strHeight, _bIsFancyWindow)
{
 if (_strURL != "")
  return PTCommonOpener.JSUTIL_VERSIONOBJ.PTWindowUtil.openWindow(_strURL, _strWindowName, _strHeight, _strWidth, _bIsFancyWindow);

 newwin.location=_strURL;
 newwin._strWindowName=_strWindowName;
 newwin._strWidth=_strWidth;
 newwin._strHeight=_strHeight;
 newwin._bIsFancyWindow=_bIsFancyWindow;
 window.setTimeout("PTCommonOpener.handlePopup();",50);
 return newwin;
}

Update 7/17/2010: Thanks to Joel Collins for helping out with some of the code here and building the solution’s binaries! Hopefully you find them useful so you can just deploy the .DLL without having to compile it yourself. Download DisplayFilter.zip for the binaries (source and config files included).

Tags: , , , ,

Leave a Reply