+ Start a Discussion
Doug ACFDoug ACF 

Issue Attaching Page Content

I'm having trouble creating a record and then attaching the content of a Visualforce page as an attachment to that record.  In the sample below, I have three methods: one that creates a contact, one that renders the page and attaches it to the contact, and one that calls those two methods in sequence.

 

Apex class:

public class tcshelper {

 

public static void processContact(Id acctId) {

ID conID = addNewContact(acctId);

attachPage(conId);

}

 

public static ID addNewContact(Id acctId) {

Contact con = new Contact();

con.lastname = 'Smith';

con.accountid=acctID;

insert con;

return con.id;

}

 

public static void attachPage(Id conId) {

PageReference blobPage = Page.tcstest;

blobPage.getParameters().put('id',conId);

Blob txtBlob = blobPage.getContent();

Attachment a = new Attachment(parentId = conId, name= 'conpage.txt', body = txtBlob);

insert a;

}

}

Visualforce page:

 

<apex:page standardcontroller="account">

<apex:detail />

</apex:page>

 

When I execute createNewContact or attachPage individually, they work fine (the contact attachment has the detail section displayed).  But when I execute the top method processContact, the resulting attachment contains the message:

 

Data Not Available
The data you were trying to access could not be found. It may be due to another user deleting the data or a system error. If you know the data is not deleted but cannot access it, please look at our support page.

It seems like the page is not able to find my newly created contact.  What am I missing?
Message Edited by Doug ACF on 01-31-2009 09:02 AM
mtbclimbermtbclimber
What is calling processContact ?
Doug ACFDoug ACF

Andrew -

 

This is just a simplied prototype I created to reproduce the issue, so I have just been executing it from within the IDE.  But in the real app we are currently invoking the initial method from a button's on-click Javascript using the AJAX sforce.apex.execute.

mtbclimbermtbclimber

Ok, I'll assume this is because you are presenting no UI of any kind to the user. I had been building up an example Visualforce page that would host the button but that doesn't seem appropriate in this case.

 

The thing you need to understand is that PageReference.getContent() is processed in a separate, internal request on our side which is, unfortunately, unaware of the modifications made above the invocation in the method processing.

 

Given your objective the solution is to break this into 2 services/requests. The first returning the new record Id which you can pass to the second that calls to getContent().

Doug ACFDoug ACF

Yes that's right, there is no UI, just a custom button that creates a record and then attaches a file to the new record.  So we can just make two separate sforce.apex.execute calls.

 

However I would appreciate an example of how to deal with this from a VF page commandButton for future reference.  Is there some way to "commit" the modifications made up to a certain point in the processing such that the getContent() would have visibility to the recent database changes?

mtbclimbermtbclimber

No, there is no way to commit within a block of apex in the sense that you would require here. The Visualforce solution is similar in that it operates in 2 requests.

 

Another angle would be to put the call to getContent() into asynchronous processing using a method with the @Future annotation. There is, however, an issue that has been identified with the combination of asynchronous apex processing and the getContent() function which we are investigating. I will update this thread when that becomes a viable, and likely the best practice, solution to this issue.