Salesforce Content Primer for Implementers

April 15, 2011 Appirio

By Nitin Jain

Salesforce Content is a structured library of your organization’s business documents. This includes sales presentations, product data sheets, policy documents, proposal templates, and best practices. Salesforce Content supports the storage of many common document formats and can display a preview of most of the content without downloading it.

This Primer comes from hands-on experience implementing Salesforce Content, the challenges faced and the solutions devised during the implementation. Hopefully, implementers will save some time by learning from the tips, tricks and “gotchas” mentioned in here.

Terminology and Concepts
Here is an overview of some basic terminology and concepts in Salesforce Content:

  • Content Document: This is a wrapper object for a Content version object. It stores things like Owner and LatestPublishedVersionId.
  • Content Version: (API name is ContentVersion) This is the main object in Salesforce Content. ContentVersion is the holder of the actual content by version. The content data is stored in base64 encoded form in the VersionData field. Content Version also has many other system defined fields like Title, FileType, and Description.
  • Content Fields: In addition to system defined fields for Content, additional custom attributes (fields) can be added to the Content object.
  • Content Types: Content Types defines a business document type. This includes Training Material, Case Study, Marketing Material, etc. Content Types allows for the grouping of Content Fields using Page Layouts. Additionally, for each Content Type, a corresponding record type is also created.
  • Page Layout for Content Type: When a user selects a Content Type, the associated page layout is loaded for the user to fill-in fields defined in that layout. Click on “Edit” against the Content Types to edit the Page Layout, then drag and drop fields to the desired location on the page.
  • Picklist Customization: Content Type can also be used to customize picklists. After clicking the “Picklist” link, a list of Content Types is displayed. Select the Content Type for which you would like to customize the picklist.
  • The next page displays a list of content picklist fields. Simply select the picklist you would like to customize.
  • Workspaces: A Workspace represents an area where users can collaborate on content. Each Workspace can be shared across users.
  • Workspace Permissions: This allows you to add, edit and delete Workspace permissions.
  • Assigning Permissions to a Workspace: You can assign Workspaces to individual Salesforce CRM Content users or public groups that contain Salesforce CRM Content users.
  • Restricting Content Type for a Workspace: You can also restrict the type of content that can be delivered to a Workspace.
  • Defining Public Group for All Internal Content Users in the Org: You can build a public group to simplify the Workspace assignment process. An example use case could be; all users in the organization should have authoring and publishing permission to a “Pictures” Workspace. To do this you need to enable “All Internal Users” public group by enabling Customer Portal. Once the “All Internal Users” public group is enabled, use it to define an “All Internal Content User” public group. This new custom defined group can be then used to assign permission to the Workspace.
  • Bulk Loading Content: In order to bulk load content, follow the steps listed below:

1. Create a .csv file in the following format:

Column Name = Content Field [explanation]:

File Name = PathOnClient
[Used to determine file type e.g. Power Point, Word doc, jpg image. This is based on extension of the file name]

Document Location = VersionData
[Represents location of the actual file. Used to load the file data in the VersionData system field]

Workspace Id = FirstPublishLocationId
[This is the Workspace where the content would be initially published. Depending on the permission settings of the Workspace, the content can be shared with other Workspaces after the initial load]

2. Use Apex Data Loader to load the content using the load file created above.
– Object Name: Content Version
– Click on “Show More” to see the Content Version object.
– Use latest version of Apex Data Loader – older versions do not support Content objects.

3. Use A
uto Matching for field mapping, then use the mapping above to map file name, document location and Workspace Id. Remember to save mapping for future loads.

Tips, Tricks and “Gotchas”

1. Displaying image from Content in a Portlet: Image data is stored as base64 encoded string.

Controller code:

public transient String imageData {get; set;}

public void loadImageData() {
List content = [select id, versiondata, pathonclient
from ContentVersion where Id=:contentid];

imageData = EncodingUtil.base64Encode(content[0].versiondata);

Visualforce page code:

Note: This technique works in Firefox and other non IE browsers. IE7 does not support a base64 image display. It may be supported in future versions of IE.

2. Do not deploy Content Types (i.e. Content Layouts) using Change Sets or the ANT migration tool. This will mess up the Content Types in an irrecoverable way. The only solution at that point is to request run dbscript.

3. Subscribed Content, Workspace, Author and Tags can not be queried via API.

4. The Subscribe feature is going to be replaced by a “Follow” feature.

5. Currently you can only follow Content. Author and Tags. Workspaces can not be followed, but will most likely be supported in upcoming releases.

6. Following an Author can be achieved by following People.

7. You cannot control the order of Content filters nor the showing/hiding of certain filters.

8. Related Content attached to an object cannot be queried via SOQL or API.

9. There is no support to deploy Workspaces and Workspace permissions from a sandbox org to production or to other sandbox orgs.

10. Content Filter does not have Content Type as filter criteria [Filter by Content Type in Salesforce Content]. A possible workaround can be found below:

a. Add a custom field on Content of datatype : Text (255)

b. Write a batch Apex Class to populate custom content type field from RecordType.Name.

Batch Apex Class code:

global class ContentBatch implements Schedulable, Database.Batchable  {

global List failedContent = new List();

global void execute(SchedulableContext SC) {
ContentBatch contentBatch = new ContentBatch();
ID batchprocessid = Database.executeBatch(contentBatch);

global Database.QueryLocator start(Database.BatchableContext BC) {
return Database.getQueryLocator([SELECT id,
FROM ContentVersion
WHERE isLatest = true
AND != ''
AND content_type__c = '']);

global void execute(Database.BatchableContext BC, List batch) {

List contentList = (List)batch;

Set recordConnectionIds = new Set();
for (ContentVersion contentRecord : contentList) {
contentRecord.content_type__c =;

Database.SaveResult[] dbResults = Database.update(contentList,false);

// send failed updates via email to job submitter.
String emailSubject = 'Content Batch Errors';
String emailBody = '';
Boolean sendEmail = false;

for (Database.SaveResult dbResult : dbResults){
if (!dbResult.isSuccess()) {
Database.Error err = dbResult.getErrors()[0];
emailBody += 'n' + 'id=' + dbResult.getId() + ' Error: ' + err.getMessage();
sendEmail = true;

if (sendEmail) {

AsyncApexJob apexJob = [Select Id, Status, NumberOfErrors, JobItemsProcessed,
TotalJobItems, CreatedBy.Email
from AsyncApexJob where Id =:BC.getJobId()];
// send an email out with results.
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {apexJob.CreatedBy.Email};
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });


global void finish(Database.BatchableContext BC) {

public static void testBatch() {
ContentBatch cbatch = new ContentBatch();

// Schedule the batch for hourly run
String sch = '0 0 * * * ?';
system.schedule('Content Hourly batch',sch,cbatch);


c. Schedule the above batch apex class to run every hour or whatever frequency is appropriate for your implementation.
For scheduling , Run following command in “execute anonymous” block.

ContentBatch cbatch = new ContentBatch();
String sch = '0 0 * * * ?';
system.schedule('Content Hourly batch',sch,cbatch);

For running batch on demand, Run following command in “execute anonymous” block.

ContentBatch cbatch = new ContentBatch();

To view Scheduled Jobs
Setup > Administration Setup > Monitoring > Scheduled Jobs

To Monitor Apex Batch Jobs
Setup > Administration Setup > Monitoring > Apex Jobs

11. It appears that Salesforce Content is moving towards the concept of Files. The current Content tab will be replaced by a Files tab and Content Workspaces will be re-named Content Libraries. The Files tab would show files from Content Libraries as well as files posted to the Chatter feed.

About the Author:
Nitin Jain is a Senior Technical Consultant at Appirio, where he designs and develops business applications in’s Sales and Service Cloud and custom applications on the Custom Cloud.

Previous Article
Our Favorite Summer ’11 Features
Our Favorite Summer ’11 Features

The Summer ’11 release is due to hit an org near you in the next couple of weeks. Are you re...

Next Article
Installing Ruby 1.8 and & 2.3.8 for ActiveSalesforce

I’ve been working for the past week or two on my Ruby for Developers series but have run into a f...