Appirio's Tech Blog

Friday, April 15, 2011

Salesforce Content Primer for Implementers

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 Auto 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 salesforce.com 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, recordtype.name
                                      FROM ContentVersion
                                      WHERE isLatest = true
                                      AND recordtype.name != ''
                                      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 = contentRecord.recordtype.name;         
     }
    
 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};
         mail.setToAddresses(toAddresses);
         mail.setSubject(emailSubject);
         mail.setPlainTextBody(emailBody);
         Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
      
     }
 }
    
 global void finish(Database.BatchableContext BC) {
 }
    
 @isTest
 public static void testBatch() {
    ContentBatch cbatch = new ContentBatch();
     Database.executeBatch(cbatch);
       
  // 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();
Database.executeBatch(cbatch);

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 salesforce.com’s Sales and Service Cloud and custom applications on the Force.com Custom Cloud.

7 comments:

  1. Any tips or tricks for displaying the content preview widget in a VF page? I know you can add this tidbit of JS but it is totally not supported by SFDC:

    insertFlexComponent('/static/102010/sfc/flex/DocViewer', 'shepherd_prefix=/sfc/servlet.shepherd&v=068R00000008q3G&mode=chatterfilepreview', '400px', '400px', '#f3f3f3', 'filePreview', 'renditionLarge', false, { adobeFlashVersionLbl : 'You must enable or download Adobe Flash Player version 9.0.115 or later to use this feature.', downloadAdobeLbl : 'Download Adobe Flash Player', downloadAdobeLinkLbl : 'Click the link below to download the Adobe Flash Player:', closeDialogLbl : 'Cancel'});

    ReplyDelete
  2. I think the answer is no, but is there any way to provide a link where the public (external to Salesforce) could download content? Like you can do with Document records.

    ReplyDelete
  3. You can create a content delivery and display CD using it's url as frame in a vf page....

    ReplyDelete
  4. There is a slightly anemic Idea for this: https://sites.secure.force.com/success/ideaView?id=08730000000Bq8gAAC

    ReplyDelete
  5. @Jason : you can use following url pattern to access the image (for internal logged in users)

    /sfc/servlet.shepherd/version/renditionDownload?rendition=THUMB720BY480&versionId=068R0000000Cqg2IAC

    use above url in an img tag along with contentversion
    hope this helps.

    ReplyDelete
  6. Do you know if there is support to create Libraries from a trigger or class?

    ReplyDelete
  7. Does anyone know where I can go to find a summary of pros and cons on SFDC CRM Content? I have to present it to a group of people. Any help is greatly appreciated.

    ReplyDelete

 
2006-2012 Appirio Inc. All rights reserved.
Appirio.com | Support | Resource Center | Contact | Careers | Privacy Policy