Content Delivery API to Extend or Integrate Content

Over the last several blog posts, you’ve learned how Capricorn’s content team and site designers use Salesforce CMS to create content, curate content for their main website, and display content in visually engaging ways. Now, the Capricorn team wants to reuse some of their content, such as the promotional content shown below, on their B2B website (a Visualforce Community) as well. They do this using the Content Delivery API. In this blog post, we’ll learn how Capricorn uses the Content Delivery API and how you can follow the same steps to take advantage of the Content Delivery API.


The Content Delivery API enables you to retrieve content from Salesforce CMS and use it to build and deliver custom experiences. You can use the API to help build components in Lightning as well as reuse content from Salesforce CMS in Visualforce pages.

Prerequisites

  1. Create and Set Up Communities User permission is enabled.
  2. User is a member of the Experience/Community where this content is published.
  3. The Workspace where the content is published is active and shared to the Experience/Community.
  4. Must have access to Salesforce API.

API

The Content Delivery API is read-only with the following signature.

EndPoint

URL

GET: /connect/communities/{:communityId}/managed-content/delivery

URL parameters

Param Name (New Only) Required? Values Description Since Group
managedContentType YES String The developer name of the managed content type. 47
managedContentIds NO List<String> List of Managed content ids with the max limit as 200 47
topics NO List<String> List of topics with the max limit as 15 47
page NO int An integer specifying a page of items. The default value is 0, which returns the first page 47
pageSize NO int Specifies the number of items per page. Valid values are from 1 through 250. The default value is 25 47
language NO String Specifies the language of the content. 47

You can find more details about the API here.

Note: The Content Deliver API is supported from API Version 47.0.

There are three available approaches to using the Content Delivery API. The Capricorn team used all of them. These examples can help you decide how you want to build your customized experiences.

Approach 1: Visualforce page with CMS content inside a Visualforce Community

Prerequisites
The content you’re retrieving using the Content Delivery API is published to a community.

Step 1: Create a Visualforce-based community
Use case: The example uses Capricorn’s B2B Commerce Community. Like Capricorn, you want to customize a site by adding tabs for each content type. You can use the Content Delivery API and Visualforce. This approach is helpful if you’re already using a Visualforce-based community.

If a community is created from a Visualforce page, select the Salesforce Tabs + Visualforce template to create the community.


Step 2: Embed the custom code
Create custom code using the example code snippet. This example shows adding an Apex class that integrates the Content Delivery API and uses the contenttype (‘news’) parameter to return content.

Add a Controller Class (MCApexController) that calls the Content Delivery API as shown in the code snippet.

public with sharing class MCApexController {

    public String managedContentIdsStr { get; set; }
    public String topicNamesStr { get; set; }
    public String contentType { get; set; }
    public String errorMessage { get; set; }
    public String communityId {get; set; }
    
    public List<ConnectApi.ManagedContentVersion> results { get; set; }

    public MCApexController(){
       communityId = Network.getNetworkId();
       getMContent();

    }
    
    public void getMContent() {
       List<String> contentIds = new List<String>();
       List<String> topicNames = new List<String>();
       try{
           if(topicNamesStr != null && !String.isEmpty(topicNamesStr)){
               topicNames = topicNamesStr.split(',',-1); 
           }
           
           
           for(Integer i=0;i<topicNames.size();i++){
               topicNames.set(i,topicNames.get(i).trim());
           }

           String language = 'en_US';
           
           ConnectApi.ManagedContentVersionCollection contentList = ConnectApi.ManagedContent.getManagedContentByTopicsAndIds(communityId, contentIds, topicNames, 0, 25, language, 'news');
           results = contentList.items;           
       }
       catch(ConnectApi.ConnectApiException e){
           errorMessage = e.getMessage();
           System.debug('Error Message : '+e);
           ApexPages.addMessages(e);
           results = new List<ConnectApi.ManagedContentVersion>();
       }           
    }
}

Add a page block in an Apex page (ManagedContent) that invokes the method defined in the Apex class and returns the content.

<apex:page controller="MCApexController" docType="html-5.0" sidebar="false" showHeader="false" standardStylesheets="false" cache="false" applyHtmlTag="false" >
  <!-- Begin Default Content REMOVE THIS -->
    <apex:messages /> <!-- title="{!errorMessage}" rendered="{!NOT(errorMessage == null)}"/> -->
  <apex:pageBlock >
         <apex:repeat value="{!results}" var="mcVersion">
          <a href="{!mcVersion.contentNodes["ImageMain"]["title"]}" >
           <apex:image url="{!mcVersion.contentNodes["ImageMain"]["url"]}" width="100%" height="100%" />
          </a> 
         </apex:repeat>
  </apex:pageBlock>

Create a custom tab, then configure the Apex page that corresponds to that tab.

You can add custom tabs into a Visualforce community page from the community workspaces tab.

From the experience community URL, you can see that you published content to the community in a flexible and customized way.

The same Visualforce page can be included in the B2B Commerce Order Confirmation page, using Module Body Includes in B2B Commerce Configuration settings. The following is an example of how CMS content looks inside B2B Commerce.

Approach 2: Bespoke Aura component with CMS content in Lightning Community

Now the Capricorn team wants to customize the site so that the user provides the contenttype and contenttypeid as input. That way, the user will only be able to see content based on that input. Capricorn can do that by using component-based development. The component created can be reused in multiple communities and can coexist with other components.

Capricorn used the following steps to create an Aura component that uses the Content Delivery API. You can follow the same steps.

Step 1: Create an Aura component bundle with contenttype and contentID user input
Create an Apex class that calls the Content Delivery API to get content.

public with sharing class ManagedContentControllerForLex {
 
    @AuraEnabled
   public static List<ConnectApi.ManagedContentVersion> getMContent(String contentType, String managedContentIds_str, String topicNames_str, String language) {
       List<String> contentIds = new List<String>();
       List<String> topicNames = new List<String>();
       try{
           if(managedContentIds_str != null && !String.isEmpty(managedContentIds_str)){
               contentIds = managedContentIds_str.split(',',-1);
           }
          
           for(Integer i=0;i<contentIds.size();i++){
               contentIds.set(i,contentIds.get(i).trim());
           }
           
           if(topicNames_str != null && !String.isEmpty(topicNames_str)){
               topicNames = topicNames_str.split(',',-1); 
           }
           
           for(Integer i=0;i<topicNames.size();i++){
               topicNames.set(i,topicNames.get(i).trim());
           }
           String communityId = Network.getNetworkId();
           
           language = 'en_US';
           ConnectApi.ManagedContentVersionCollection contentCollection = null;
           contentCollection = ConnectApi.ManagedContent.getManagedContentByTopicsAndIds(communityId, contentIds, topicNames, 0, 25, language, contentType);
           return contentCollection.items;
       }
       catch(ConnectApi.ConnectApiException e){
           System.debug('Error Message : '+e);
           ApexPages.addMessages(e);
           List<ConnectApi.ManagedContentVersion> mcvList = new List<ConnectApi.ManagedContentVersion>();
           return mcvList;
       }
   }
}

Create an Aura component “McComponent.cmp” markup that the displays content.

<aura:component access='global' >
   <aura:attribute name='title' type='String' />
   <aura:attribute name='type' type='String' />
   <aura:attribute name='managedContentId' type='String' />
   <aura:attribute name='topicsList' type='Object[]' /> 
    <aura:attribute name='imgUrl' type='String' /> 
   <aura:attribute name='topicsListStr' type='String' />
    <aura:handler name="init" value="{!this}" action="{!c.do_init}"/>
 <div class='demo-only' style='width: 30rem;'>
   <article class='slds-tile'>
       <div class='slds-tile__detail'>
           <dl class='slds-list_horizontal slds-wrap'>
               <dt class='slds-item_label slds-text-color_weak slds-truncate'>Title : </dt>
               <dd class='slds-item_detail slds-truncate'>{!v.title}</dd>
               <dt class='slds-item_label slds-text-color_weak slds-truncate'>Content Type : </dt>
               <dd class='slds-item_detail slds-truncate'>{!v.type}</dd>
               <dt class='slds-item_label slds-text-color_weak slds-truncate'>Managed Content Id : </dt>
               <dd class='slds-item_detail slds-truncate'>{!v.managedContentId}</dd>
               <dt class='slds-item_label slds-text-color_weak slds-truncate'>Topics List : </dt>
               <dd class='slds-item_detail slds-truncate' >{!v.topicsListStr}</dd>  
               <dt class='slds-item_label slds-text-color_weak slds-truncate'>Image URL </dt>
               <dd class='slds-item_detail slds-truncate'>{!v.imgUrl}</dd>
           </dl>
   </div>
   </article>
 </div>
</aura:component>

Create an Aura controller for the component markup to the display content.

({
    myAction : function(component, event, helper) {
        
    },
    do_init : function(component, event, helper) {
        var topicsList = component.get('v.topicsList');
        var topicNamesListStr = "";
        for(var i=0;i<topicsList.length;i++){
            if(topicNamesListStr === ""){
                topicNamesListStr = topicsList[i].name;
            }
            else{
                topicNamesListStr = topicNamesListStr +" , "+topicsList[i].name;    
            }
        }
        component.set("v.topicsListStr",topicNamesListStr);
    }
})

Create an Aura component bundle that displays the content fetched via the Content Delivery API in a customized format.

Create an Aura component ‘RetrieveMcComponent’ that uses the contentType and ContentId input.

<aura:component controller='ManagedContentControllerForLex' implements="forceCommunity:availableForAllPageTypes" access='global'>

   <aura:attribute name='contentList' type='ConnectApi.ManagedContentVersion[]' />
    <aura:attribute name='language' type='String' />
    <aura:attribute name='contentType' type='String' />
    <aura:attribute name='managedContentIds' type='String' />
    <aura:attribute name='topicNames' type='String' />
   <aura:handler name='init' value='{!this}' action='{!c.doInit}' />
   
   <ui:inputText label="Content Type : " class="field" value="{!v.contentType}"/>
   <ui:inputText label="Enter ',' seperated ManagedContentIds : " class="field" value="{!v.managedContentIds}"/>
   <ui:inputText label="Enter ',' seperated Topic Names : " class="field" value="{!v.topicNames}"/>
   <ui:button aura:id="button" buttonTitle="Click to get M Conent" class="button" label="Get MContent" press="{!c.getMcContent}"/>
    <aura:if isTrue="{!v.contentList.length != 0}">
    <aura:iteration items='{!v.contentList}' var='record'>

        <c:McComponent title='{!record.title}' type='{!record.typeLabel}' managedContentId='{!record.managedContentId}' topicsList = '{!record.associations.topics}'  imgUrl = '{!record.contentNodes.bannerImage.url}'/>
        <br/>

   </aura:iteration>
    </aura:if>

</aura:component>

Create an Aura Controller that fetches content from ContentType and ContentId.

({
    myAction : function(component, event, helper) {
        
    },
    doInit : function(component, event, helper) {
    },
    getMcContent : function(component, event, helper) {
        var selectedCType = component.get('v.contentType');
        var selectedMCIds = component.get('v.managedContentIds');
        var selectedTopics = component.get('v.topicNames');
        var selectedLanguage = component.get('v.language');
        var contentAction = component.get("c.getMContent");
        if(selectedMCIds){
            contentAction.setParam('managedContentIds_str', selectedMCIds);    
        }       
         contentAction.setParam('topicNames_str', selectedTopics);
         contentAction.setParam('language', 'en_US');    
        if(selectedCType){
            contentAction.setParam('contentType', selectedCType);    
        }
        contentAction.setCallback(this, function(action) {
                      var state = action.getState();
                    if (state === 'SUCCESS') {
                        component.set("v.contentList",action.getReturnValue());
                    }
                    else{
                        console.log("Error occurrred");
                    }
                   });
        $A.enqueueAction(contentAction);
    }
    
})

The components you created can now be added into an experience/community from experience builder.

Step 2: Add Aura component to community or app
Add the components you created to the community using the experience builder. Then publish the community.

When accessing the community URL, the user will be able to access the content by providing contenttype and contentId in a flexible and customized way.
More details of adding an Aura component can be found in here.

Approach 3: Bespoke Lightning Web Components (LWC) with CMS content in Lightning Community

Now the Capricorn team wants to customize the site using Lightning Web Components, which has performance improvements compared to Aura Components. The user will be able to see content created in CMS in Lightning Communities in more customized way. The component created can be reused in multiple communities and can coexist with other components.

Capricorn used the following steps to create a Lightning Web component that uses the Content Delivery API. You can follow the same steps.

Prerequisites:
The content you’re retrieving using the Content Delivery API is published to a community.

Step 1: Create a Lightning WebComponent bundle with Content fetched from CMS via Content Delivery API
Create an Apex class that calls the Content Delivery API to get content.

public with sharing class MCWrapperController {

    @AuraEnabled(cacheable=true)
    public static List<ConnectApi.ManagedContentVersion> initMethod(){
        MCController mcController = new MCController();
        return mcController.results;
    }

    public class MCController{
        @AuraEnabled public String managedContentIdsStr { get; set; }
        @AuraEnabled public String topicNamesStr { get; set; }
        @AuraEnabled public String contentType { get; set; }
        @AuraEnabled public String errorMessage { get; set; }
        @AuraEnabled public String communityId {get; set; }
        @AuraEnabled public List<ConnectApi.ManagedContentVersion> results { get; set; }

        public MCController(){
            communityId = Network.getNetworkId();
            getMContent();
        }
        
        public void getMContent() {
        List<String> contentIds = new List<String>();
        List<String> topicNames = new List<String>();
        try{
            if(topicNamesStr != null && !String.isEmpty(topicNamesStr)){
                topicNames = topicNamesStr.split(',',-1); 
            }
            
            
            for(Integer i=0;i<topicNames.size();i++){
                topicNames.set(i,topicNames.get(i).trim());
            }
           
            String language = 'en_US';
            //Note: "news" is the contenttype of the content value hard coded in the code here.
            ConnectApi.ManagedContentVersionCollection contentList = ConnectApi.ManagedContent.getManagedContentByTopicsAndIds(communityId, contentIds, topicNames, 0, 25, language, 'news');
            results = contentList.items;           
        }
        catch(ConnectApi.ConnectApiException e){
            errorMessage = e.getMessage();
            System.debug('Error Message : '+e);
            ApexPages.addMessages(e);
            results = new List<ConnectApi.ManagedContentVersion>();
        }           
        }
    }   
}

MCWrapperController.cls-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="urn:metadata.tooling.soap.sforce.com" fqn="MCController">
    <apiVersion>47.0</apiVersion>
    <status>Active</status>
</ApexClass>

Create an LWC component bundle with folder name “cmsContent”. Create “cmsContent.js” which uses @wire to call an Apex class and get content.

cmsContent.js

import { LightningElement, wire } from 'lwc';

import initMethod from '@salesforce/apex/MCWrapperController.initMethod';

export default class cmsContent extends LightningElement {
    @wire(initMethod ) results;

     /* my custom function, will use it later */
     get jsonData() { 
        return this.results;
    }
}

cmsContent.js-meta.xml
Specify target as <target>lightningCommunity__Page</target> to make the component available in Community Builder.

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="cmsContent">
    <apiVersion>47.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
        <target>lightningCommunity__Page</target>
        <target>lightningCommunity__Default</target>
        <target>lightning__AppPage</target>
    </targets>
</LightningComponentBundle>

cmsContent.html

<template>
    <lightning-card title="CMS Content">
        <template for:each={jsonData.data} for:item="content">
                <span key={content.managedContentId}>{content.contentNodes.title.value}</span>
        </template>
    </lightning-card>
</template>

Deploy the Lightning web component using SFDX to the Salesforce org. More details about deployment can be found here.

The component created can now be added into the community from experience builder.

Step 2: Add the LWC component to the community using the experience builder
Add the LWC component created to the community using the experience builder. Then publish the community.

More details of adding a Lightning web component can be found here.

The Content Delivery API provides more flexibility and an extensible way to use content to build a richer experiences for customers.

References

Blog posts in this series

About the author

Pradeep Kumar Saraswathi
Pradeep is working as LMTS at Salesforce.org, based in Hyderabad, India. He has been working on UI for the last several years architecting, developing and improving User Experience. You can connect with Pradeep on LinkedIn.

Leave your comments...

Content Delivery API to Extend or Integrate Content