Friday, April 16, 2010

Salesforce Content - Programmatically Uploading Content

Building up to this through a series of recent posts, I've demonstrated how to set up and configure a workspace and how to work with Content Types and publish using Guided Tagging. This post will go through the steps to programmatically insert Content related to a Custom Object within the previously defined workspace and using the previously defined Content Types.

The first step is to write your Apex Code (remember to use version 17.0 or higher of the Salesforce API). Here's the code:

public class ImageController {

public ContentVersion myImage {get;set;}
Bird__c bird;

Map workspaceMap = new Map {
'Species Images' => '058S0000000CaaY'
};
Map contentTypeMap = new Map{
'Species Images' => '012S00000000Kpe'
};
public ImageController(ApexPages.StandardController controller){
bird = (Bird__c)controller.getRecord();
bird = [select Id, Name, Binomial_Nomenclature__c from Bird__c where Id = :bird.Id ];
myImage = new ContentVersion();
}
public PageReference upload(){
if (myImage.PathOnClient != null){
myImage.Title = bird.Name;
myImage.RecordTypeId = contentTypeMap.get('Species Images');
myImage.FirstPublishLocationID = workspaceMap.get('Species Images');
myImage.Bird__c = bird.Id;
insert myImage;
return new PageReference('/'+ bird.Id);
}
else{
return null;
}
}
public PageReference cancel(){
return new PageReference('/' + bird.Id);
}
}

There's a member variable for the Content (myImage), two member maps to get the right IDs for the Workspace and the ContentType, two Page Actions (one to save, the other to eliminate a cul-de-sac and allow the user to return to the Bird layout) and a constructor that fills the Context record and instantiates the ContentVersion.

Now you write a VF Page (also using Version 17.0 or higher of the API). Here's that code (I removed the leading "<"s from the apex tags to allow this to show inline.

apex:page standardController="Bird__c" extensions="ImageController">
apex:pageBlock title="Upload Image">
apex:pageMessages />
apex:form >
apex:inputFile value="{!myImage.VersionData}" filename="{!myImage.PathOnClient}"/>
br />
apex:commandButton action="{!upload}" value="Upload"/>
apex:commandButton action="{!cancel}" value="Cancel"/>
/apex:form>
/apex:pageBlock>
/apex:page>

This is a simple page that uses the Bird object as the standard controller, uses the Apex code we wrote before as the extension and has a form. The form has an inputFile tag to allow you to browse to the image using explorer and two command buttons that call the page actions defined in our previous page.

If you like, you can add to the page action for upload the error condition and put that in your page messages. this allows for elegant trapping when you don't browse to a file. The final step is to wire all this up.

Add a Custom Button to the Bird Object that uses the Visualforcepage as the Content Source



Then add that button to your Custom Object's layout




Note the presence of the button with no image



And upload the image to an instance of your Custom Object using the VF page



And finally, note that the image is there and related to your object instance



And the picture of the picture related to the Bird (a pretty Yellow-crowned Euphonia)



Voila...

P.S. - Note that you'll need to add the Class and the Page to any profiles using this capability, otherwise you'll get the fun 'Insufficient Privileges' puke

9 comments:

Scala Leaner said...

Wow... Nice article. I am looking for such a beautiful and steps to create the images in the contact. I think that the fresher like me will able to do it.
Hats off to you. I started following u (senthil_rect) in twitter.

Kari said...

Do you know anything about why there is a maximum file size of 10MB only when uploading content using this custom button, but no limit when adding content directly to the workspace tab?

Gabe Tuerk said...

Karl
I expect it relates to ViewState limitations on the Visualforce page. Uploading through 'Contribute' goes through a Java app (servlet?) using the API rather than native VF and Apex. For one-off uploads, best to use the tools Salesforce provides
G

Harshesh said...

Hi

Nice post. This has been very helpful to me but i have one question regarding the access.
In the article when adding the member to workspace it has been done manually. Can that be done using API/code? I am implementing this for large org and its not possible to manually add the users or add public group for the users and I am stuck there. Is there any way we can do it through code?

Ramen said...
This comment has been removed by the author.
Ramen said...
This comment has been removed by the author.
Venkata Narasimha Rao Vutla said...

HI,

Good Explanation, and very nice article. Thanks for sharing.

(nara.dotnet@gmail.com)

Gice said...

Hi Gabe,
I stumbled across this post after googling around quite a bit on associating CRM content with Custom Objects. I have a question though. In your example, you set the Bird__c member of the ContentVersion object, however this is not a standard association. Does apex allow you to do this, or how did you get the ContentVersion to allow that relationship with your custom object? Sorry if this is a silly question, all this is pretty new to me.
Thanks!
G

Gice said...
This comment has been removed by the author.