function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
hemmhemm 

Obtaining a VF page from a Page Layout without popping up a new window

I have created a VisualForce page that generates a file.  The contenttype parameter has a mime type for the file.  When I run the VF page on its own, it works great.  The file open/save as dialog comes up.

Now I want to be able to click a button on a Page Layout (not a VF page, but a Page Layout), run the VF page that generates the file and have the user prompted to open/save the file.  And I'd like the user to stay on the same record they clicked the button from.  I also don't want any windows popping up when teh button is clicked.  I want the user experience to be as if they are downloading a file; only it's a VF page generating a file on the fly.  I tried this using onClick JavaScript using 2 approaches.  Neither is working.  I could use some help on this or some other suggestions.  The worst case is that I use a button that opens a small window and hopefully add JavaScript to the page that will automatically close it (can i do that?)

Approach 1: AJAX toolkit's remoteFunction method.  When I run this, a big debug window pops up and says the page is being redirected to some odd URL.

Code:
{!requireScript("/soap/ajax/14.0/connection.js")}
sforce.connection.sessionId="{!$Api.Session_ID}";

sforce.connection.remoteFunction({
url : "https://namespace.na4.visual.force.com/apex/vfpage?id={!Lead.Id}",
async: true,
onSuccess : function(response) { }
});

 
Approach 2: Making a JavaScript XMLHttpRequest. This requires me to add a Remote Site for the force.com VisualForce site.  Also, I get a 302 error due to that same redirect.

Code:
xmlhttp=null;
if (window.XMLHttpRequest) {
   xmlhttp=new XMLHttpRequest();
} else if (window.ActiveXObject) {
   xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}

if (xmlhttp!=null) {
   xmlhttp.onreadystatechange=state_Change;
   xmlhttp.open("GET","/apex/vfpage?id={!Lead.Id}",true);
   xmlhttp.send(null);
} else {
   alert("Your browser does not support XMLHTTP.");
}

function state_Change() {
   if (xmlhttp.readyState==4) {
      if (xmlhttp.status==200) {
         // Got it!
      } else {
         alert("Problem retrieving vCard data: " + xmlhttp.status);
      }
   }
} 

 
A completely different approach (Approach 3) that I was thinking could work would be to embed a new VisualForce page into my Page Layout.  That VF page would only have a button on it.  I am hoping that that page could make the call to the main VF page using the PageReference object.  I can't seem to get it to work.  I went through so many iterations of code I can't post a good example on this one.



Message Edited by hemm on 11-15-2008 11:30 PM
LessLess
So what you want to do is trigger a file download from one of your pages without reloading the screen or popping a window. There is an older, somewhat neat way to do this... There is one caveat though: You can't have "developer mode" turned on and neither can the folks using this. (I'll explain why in a bit...)
 
 
The basic idea is that you call an inline JavaScript function which'll rewrite the calling page to dump a form tag under some arbitrary section then calling that form tag (script injection). Since you're controlling the page that you want this script to load upon and the domain that you want to end upon, you can rest assured that it isn't anything "immoral"...
 
I'll show you the page/scripts, then put it all together.
  1. Since I'm not sure what your page does, I went ahead and created an arbitrary test page that just passes back date as "application/octet" (Arbitrary File):
    "test" (Visualforce page) 
    <apex:page contentType="application/octet">
      <!-- Begin Default Content REMOVE THIS -->
      <h1>Congratulations</h1>
      This is your new Page
      <!-- End Default Content REMOVE THIS -->
    </apex:page>
  2. Go to your object and create a Custom Detail Page Button. You want behavior: "Execute JavaScript" and Content Source : "OnClick JavaScript". The script is pretty easy:
    Custom Button (on your object):
    function EmbedFormHelper() {
    
        this.formSubmit = i_formSubmit;
    
        function i_formSubmit(ivalue) {
            var saveList = document.getElementsByTagName("td");
            alert(saveList.length);
            saveList[20].innerHTML += "<form name=\"exportAction\" method=\"post\" action=\"<VF_PAGE_ADDRESS>test?id"+ivalue+"\"></form>";
            document.forms.exportAction.submit();
        }
    }
    
    var wrapper = new EmbedFormHelper();
    wrapper.formSubmit("{!<YOUR_OBJECTS.ID>}");

    (BTW - this could be a lot simpler, inlining the functions. Also, you might consider modifying it to look for the form named "exportAction" since once the form is submitted it'll just complain about duplicate tags, etc...)

  3. Add the button to your page layout and save it.

How it works

The script gets called when you press the button. Since Salesforce won't allow us use getElementById() for an on-click JavaScript, we'll use a more rudimentary thing to grab any available tag - in our case a "<td>" tag. You could also grab div's or spans, or whatever, but it doesn't really matter... Once you've got a tag, the script just appends the form tag contents. This contains your page and a way to parse the ID that you're passing in. Finally, the script calls the form that if just embedded with the ID as a parameter.

Once the form calls your page, your VF page executes. If the response truly doesn't have any HTML/TEXT/XML data, and contains only a content type of "application/octet", you'll end up remaining on the same page and triggering the download dialogue. If "developer mode" is on, the response object will contain HTML data and that will trigger the browser to display that stuff and redirect you off of your calling page. 

I'm curious how this'll work out for you - could you post your approach and if this worked for you back here?



Message Edited by Less on 11-17-2008 01:05 AM
hemmhemm
Less, you rock!  Works like a charm. 

Also, if I add &core.apexpages.devmode.url=1 to the end of the URL, it doesn't matter whether or not someone is in Development Mode.  I found that hack on the forums and it seems to turn off development mode on a specific page.  It's unsupported, but the hack was posted by someone at salesforce, so I am using it.

Below is how I modified your code that you posted.  I highlighted the spots where I modified your code.
  1. Custom Button (on your object):
    function EmbedFormHelper() {

    this.formSubmit = i_formSubmit;

    function i_formSubmit(ivalue) {
    var saveList = document.getElementsByTagName("td");
    alert(saveList.length);
    saveList[saveList.length - 1].innerHTML += "<form name=\"exportAction\" method=\"post\" action=\"\/apex\/test?id=" + ivalue + "&core.apexpages.devmode.url=1\">";
    document.forms.exportAction.submit();
    }
    }

    var wrapper = new EmbedFormHelper();
    wrapper.formSubmit("{!Lead.Id}");


LessLess

Glad to help out and glad that it worked for you and glad to learn that you can force developer mode off!

BTW, you can catch the second click of the button using:

Insert this before the section that adds the form code:
        if (document.forms.exportAction) {
            alert("Second Click");
            return;
        }