# developer CMS Developer Guide (cms) - Full Documentation # Access CMS Content with a Connected App A connected app is a framework that enables an external application to integrate with Salesforce using APIs and standard protocols. To share CMS content with a system that doesn’t have a direct integration with CMS, develop a connected app. A connected app can access the CMS content and import it to the other system. ## Before You Begin Before you develop a connected app, complete steps 1 and 2 in [Display CMS Content in Other Systems](https://developer.salesforce.com/docs/platform/cms/guide/cms-dev-display-cms-content-in-other-systems.md). ## Retrieve the CMS Content To develop a connected app that copies CMS content to another system such as Marketing Cloud or Heroku, perform these steps. - [Authenticate a Connected App with CMS](https://developer.salesforce.com/docs/platform/cms/guide/cms-dev-authenticate-a-connected-app-with-cms.md) - [Retrieve CMS Channels with a Connected App](https://developer.salesforce.com/docs/platform/cms/guide/cms-dev-retrieve-cms-channels-with-a-connected-app.md) - [Retrieve CMS Content with a Connected App](https://developer.salesforce.com/docs/platform/cms/guide/cms-dev-retrieve-cms-content-with-a-connected-app.md) - [Search CMS Content with a Connected App](https://developer.salesforce.com/docs/platform/cms/guide/cms-dev-search-cms-content-with-a-connected-app.md) ## Import CMS Content to Another System After you retrieve the CMS content, the remaining steps vary based on the system that you want to share content with. Converting the CMS content to another format is a possibility. To determine the necessary steps, read the receiving system’s documentation. ## See Also - [Import and Display CMS Content in Marketing Cloud](https://developer.salesforce.com/docs/platform/cms/guide/cms-dev-import-and-display-cms-content-in-marketing-cloud.md) - [_Salesforce Developers Blog_: Build Static Sites with Salesforce CMS Headless APIs](https://developer.salesforce.com/blogs/2020/10/build-static-sites-with-salesforce-cms-headless-apis-and-heroku.html) # Add CMS Connect (HTML) Components to Your Site Pages After you set up your CMS connection and add a header and footer, you're ready to add components to your pages. 1. Open Experience Workspaces. 2. Click **Content Management** | **CMS Connect**. 3. Click ![Action Selection Arrow](https://developer.salesforce.com/docs/media/cms_arrow_select.png) next to the connection that has the components you want to add, and choose **Use in Builder**. 4. Navigate to the page you want. 5. Drag a CMS Connect (HTML) component to where you want to display it on the page. 6. In the property editor for the component, [configure its properties](https://help.salesforce.com/s/articleView?id=experience.rss_cms_connect.htm). **See Also** - [CMS Connect Examples](https://developer.salesforce.com/docs/platform/cms/guide/cms-connect-examples.md) # Add CMS Connect (JSON) Components to Your Site Pages After you set up connections to your JSON CMS content, display the content on your site pages just the way you want, using the property editor in Experience Builder to customize your component layout. 1. In Setup, enter `Digital Experiences` in the Quick Find box, then select **All Sites**. 2. Next to the site where you want to add CMS content, click **Builder**. 3. Click ![Components icon](https://developer.salesforce.com/docs/media/components_icon.png) to open the Component panel. 4. Drag a CMS Connect (JSON) component to where you want to display it on the page. 5. In the component’s property editor, [configure component properties](https://help.salesforce.com/articleView?id=rss_cms_connect_json.htm). ## Example Let’s say you want to display a list of blogs on your page. ![Content list display](https://developer.salesforce.com/docs/media/cms_content_list_runtime.png) 1. In the property editor, select the CMS source you defined as a connection. Select a content list as your JSON content. ![JSON property editor part 1](https://developer.salesforce.com/docs/media/cms_json_component_prop_p1.png) 2. Define the grid layout for your list. ![JSON property editor part 2](https://developer.salesforce.com/docs/media/cms_json_component_prop_p2.png) 3. To configure the layout of each list item, enter JSON expressions in that section. ![JSON property editor part 3](https://developer.salesforce.com/docs/media/cms_json_component_prop_p3.png) 4. Configure how you display each blog item, either through a site page or an external link. ![JSON property editor part 4](https://developer.salesforce.com/docs/media/cms_json_component_prop_p4.png) After you save the settings, you see a preview of the content in the page area. ![JSON in Builder](https://developer.salesforce.com/docs/media/cms_json_design_time.png) ## Detail Page Notice the Read More link for each item. After you publish your changes, you can click a link under a blog item in the series. When you click the link, you see an external page or a site page that’s created dynamically for each content item in your content list. To change the name or URL of this detail page, locate the page in Experience Builder in the Pages menu in the top toolbar, and modify its properties. For search engine optimization (SEO), detail page URLs are added to the sitemap files. For each content type name, only one detail page is created. If you create more than one content list under the same content type name, you see the same detail page. ## See Also - [Reuse Content with CMS Connect JSON](https://developer.salesforce.com/docs/platform/cms/guide/cms-connect-intro-json.md) - [CMS Connect (JSON) Expressions](https://developer.salesforce.com/docs/platform/cms/guide/cms-connect-json-expressions.md) - [CMS Connect Examples](https://developer.salesforce.com/docs/platform/cms/guide/cms-connect-examples.md) # Add CMS Header and Footer Components to Your Experience Builder Site After you set up your CMS connection and add a header and footer, you're ready to add components to your pages. 1. From Experience Builder, go to **Settings** | **CMS Connect**. 2. Select a header source, and enter a header path. 3. Select a footer source, and enter a footer path. ![CMS Connect header and footer settings](https://developer.salesforce.com/docs/media/cms_builder_header_footer.png) # Add External CMS Content to Your Experience Builder Pages Add your headers, footers, banners, blogs, HTML, JSON, and other content from your external CMS to your site pages. # Add Image CMS Content to Marketing Cloud Use the Marketing Cloud API’s content assets resource to add image CMS content to Marketing Cloud in a connected app. If the request is successful, the content is then available in Content Builder. ## Before You Begin To form the API request, you need these items. - A Marketing Cloud access token - The Base64-encoded image downloaded from CMS that you want to import to Marketing Cloud - The category ID of the folder in Content Builder where you want Marketing Cloud to save the image ## API Request Details To send an API call to add an image to Marketing Cloud, make an HTTP POST request to the `/asset/v1/content/assets` resource. ### Headers Include an Authorization header with the value `Bearer {access_token}`, where _access_token_ is your CMS access token. ### Request Body In the request body, create a JSON object with these fields. - `name`—A string that contains the name of the image. - `assetType`—An object containing the `id` field with a value that matches the asset type of the image format, such as GIF or JPEG. For a list of possible types, see [Marketing Cloud API: List of Asset Types](https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/base-asset-types.htm). - `file`—A string that contains the Base64-encoded image. - `category`—An object that contains the `id` field with a string value. Set the value to the category ID of the Content Builder folder where you want Marketing Cloud to save the image. For more information on the request and response format for this resource, see [Marketing Cloud API: REST API v1 Reference](https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/routes.htm#detail_CreateAsset). ## Sample Request This Node.js sample downloads an image file from CMS and then adds the image to Marketing Cloud. The `currentNode` object contains the CMS content. ```javascript async function moveImageToMC(name, currentNode, mcAuthResults, cmsAuthResults) { return new Promise(async (resolve, reject) => { const imageUrl = `${cmsAuthResults.instance_url}${currentNode.resourceUrl}`; const base64ImageBody = await downloadBase64FromURL(imageUrl, cmsAuthResults.access_token); console.log(`Uploading Image to MC: ${name} - ${imageUrl}`); let imageAssetBody = { name: name, assetType: { id: getImageAssetType(currentNode.fileName), }, file: base64ImageBody, category: { id: "1520210", }, }; // Create MC Asset await createMCAsset(mcAuthResults.access_token, imageAssetBody); resolve(); }); } function getImageAssetType(imageName) { let assetTypeResults = "8"; let fileNameChunks = imageName.split("."); let imageExtension = fileNameChunks[fileNameChunks.length - 1]; switch (imageExtension.toLowerCase()) { case "gif": assetTypeResults = 20; break; case "jpeg": assetTypeResults = 22; break; case "jpg": assetTypeResults = 23; break; case "png": assetTypeResults = 28; break; default: break; } return assetTypeResults; } async function downloadBase64FromURL(url, access_token, callback) { return new Promise((resolve, reject) => { https .get(url, { headers: { Authorization: "Bearer " + access_token } }, (resp) => { resp.setEncoding("base64"); let imageBody = ""; resp.on("data", (data) => { imageBody += data; }); resp.on("end", () => { console.log("end"); resolve(imageBody); }); }) .on("error", (e) => { reject(`Got error: ${e.message}`); }); }); } async function createMCAsset(access_token, assetBody) { return new Promise((resolve, reject) => { request.post( mc_host + mc_assets_api_path, { headers: { Authorization: "Bearer " + access_token, }, json: assetBody, }, (error, res, body) => { if (error) { console.error(error); console.log({ assetBody }); reject({ error }); } else { console.log(`statusCode: ${res.statusCode}`); console.log(body); resolve(res); } }, ); }); } ``` ## API Response In response to the API request, the API returns an HTTP response code and a JSON body with an object representing the newly created asset in Marketing Cloud. For details on the response object and possible HTTP response codes, see [Marketing Cloud API: REST API v1 Reference](https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/routes.htm#detail_CreateAsset). ## See Also - [_Marketing Cloud Package Management_: Access Token for Server-to-Server Integrations](https://developer.salesforce.com/docs/atlas.en-us.mc-app-development.meta/mc-app-development/access-token-s2s.htm) # Add Text CMS Content to Marketing Cloud Use the Marketing Cloud API’s content assets resource to add text CMS content to Marketing Cloud in a connected app. If the request is successful, the content is then available in Content Builder. ## Before You Begin To form the API request, you need these items. - A Marketing Cloud access token - The JSON object exported from CMS that contains the text CMS content that you want to import to Marketing Cloud - The category ID of the folder in Content Builder where you want Marketing Cloud to save the content ## API Request Details To send an API call to add text CMS content to Marketing Cloud, make an HTTP POST request to the `/asset/v1/content/assets` resource. ### Headers Include an Authorization header with the value `Bearer {access_token}`, where _access_token_ is your CMS access token. ### Request Body In the request body, create a JSON object with these fields. - `name`—A string that contains the name of the content. - `assetType`—An object containing the `id` field with a numeric value of `196`. 196 is the asset type for text blocks. - `content`—The JSON object for the text CMS content. - `category`—An object that contains the `id` field with a string value. Set the value to the category ID of the Content Builder folder where you want Marketing Cloud to save the content. For more information on the request and response format for this resource, see [Marketing Cloud API: REST API v1 Reference](https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/routes.htm#detail_CreateAsset). ## Sample Request This Node.js sample adds text CMS content to Marketing Cloud. The `currentNode` variable contains the JSON object of the CMS content. ```javascript async function moveTextToMC(name, currentNode, mcAuthResults) { let text = currentNode; if (typeof currentNode === "object") { text = currentNode.value; } console.log(`Uploading text to MC: ${name} - ${text}`); let textAssetBody = { name: name, assetType: { id: 196, }, content: text, category: { id: "1099065", }, }; // Create MC Asset await createMCAsset(mcAuthResults.access_token, textAssetBody); } async function createMCAsset(access_token, assetBody) { return new Promise((resolve, reject) => { request.post( mc_host + mc_assets_api_path, { headers: { Authorization: "Bearer " + access_token, }, json: assetBody, }, (error, res, body) => { if (error) { console.error(error); console.log({ assetBody }); reject({ error }); } else { console.log(`statusCode: ${res.statusCode}`); console.log(body); resolve(); } }, ); }); } ``` ## API Response In response to the API request, the API returns an HTTP response code and a JSON body with an object representing the newly created asset in Marketing Cloud. For details on the response object and possible HTTP response codes, see [Marketing Cloud API: REST API v1 Reference](https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/routes.htm#detail_CreateAsset). ## See Also - [_Marketing Cloud API Developer Guide_: List of Asset Types](https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/base-asset-types.htm) - [_Marketing Cloud Package Management_: Access Token for Server-to-Server Integrations](https://developer.salesforce.com/docs/atlas.en-us.mc-app-development.meta/mc-app-development/access-token-s2s.htm) # Authenticate a Connected App with CMS Generate OAuth credentials, and send an HTTP request to the Salesforce REST API with those credentials to authenticate a connected app with CMS. If the request is successful, the response contains an access token to use in your code for future API requests. ## Generate Authentication Credentials Generate CMS authentication credentials for the app. 1. From Setup, in the Quick Find box, enter `Apps`, and select **App Manager**. 2. Click **New Connected App** and enter the details for your app. 3. Enable OAuth for the connected app, and enter a callback URL. CMS directs the connected app to that address after authentication completes, so this URL must have corresponding code in your app. 4. Click **Save** and then click **Continue**. In the API section, save the Consumer Key and Consumer Secret because you need them in your app code. ## API Request Details To send an API call to retrieve an access token for CMS, make an HTTP POST request to the `https://login.salesforce.com/services/oauth2/token` endpoint. ### Request Body In the request body, include these parameters. - `client_id`—The client ID from when you generated OAuth credentials for your connected app. - `client_secret`—The client secret from when you generated OAuth credentials for your connected app. - `grant_type`—The type of authentication to perform. In this case, set it to `password`. - `username`—A user with access to the content you want to share. - `password`—The password of a user with access to the content that you want to share. ### Sample Request This Node.js sample requests an access token for CMS. ```javascript const fetch = require("node-fetch"); const { URLSearchParams } = require("url"); const url = "https://login.salesforce.com/services/oauth2/token"; const client_secret = "596F3F3086469C65DB9D758F4BB2E735F07493EE863AB6494E377813F1661B51"; const client_id = "3MVG95jctIhbyCppOWtBpsf0vLoREEAlrw69845a8by75Cy5.b8eVZyoR_L6iEVCBKUTcAs6twM0HHm2HJ3AO"; const grant_type = "password"; const username = "cms.heroku@salesforcecms.com"; const password = "lath9haih!WHASSI3Zppj63g8ypJF6l3qvIKEp"; //redirect_uri:test.com module.exports = async function () { return await authenticate(); }; function authenticate() { return new Promise((resolve, reject) => { const params = new URLSearchParams(); params.append("client_id", client_id); params.append("client_secret", client_secret); params.append("grant_type", grant_type); params.append("username", username); params.append("password", password); fetch(url, { method: "POST", body: params, }) .then((res) => res.json()) // expecting a json response // .then((json) => console.log(json)) .then((json) => resolve(json)) .catch((err) => { console.log({ err }); reject(err); }); }); } ``` ## API Response The response to the authentication request contains a JSON object that includes the `access_token` field. Use the access token to authenticate in future API requests. This sample is the response body from an authentication request. ```javascript { "access_token": "00D5w000000VQ9w!ARkAQAAiui9fznMxOuWyEVTJ_VKUY.HDoe0GOamm8R518j1EJ7WSltj8DmsQk2SafUJfIfYm2JgQM3dVSIqi0asJVYeOZn0i", "instance_url": "https://referencecmsdemo2020.my.salesforce.com", "id": "https://login.salesforce.com/id/00D5w000000VQ9wEAG/0055w00000BdhGbAAJ", "token_type": "Bearer", "issued_at": "1590775858562", "signature": "b5xxFxkNZ2BPircw9NAV5sQffg52oEaaO+5jHXaNs/I=" } ``` # B2C Commerce Cloud Component Types for CMS Content B2C Commerce Cloud uses components to display content. To include CMS content in components, define a new component type for each CMS content type. Each component type requires a definition file, a JavaScript file, and a template file. ## Definition File The definition file specifies information about the component, such as the display name when you select the component in Page Designer. To indicate that the component uses CMS content, create an object in the attribute_definitions array that defines the content type’s attributes. In that object, set the type field to `cms_record` and the editor_definition object’s `content_type` field to the name of the content type. This sample definition file creates a component type for CMS content. The CMS content uses a custom content type called “Promotion.” The `attribute_definition_groups.id` field has a value of `cmsPromotion`, which is used in the JavaScript file to reference the CMS content in the component. ```javascript { "name": "CMS Promotion Banner", "description": "Image, text overlay from CMS that links user to a B2C target using the markup editor", "group": "assets", "attribute_definition_groups": [{ "id": "promotionContent", "name": "Promotion Content", "description": "The presentation of the promotion", "attribute_definitions": [{ "id": "cmsPromotion", "name": "Promotion Content", "type": "cms_record", "required": true, "editor_definition": { "content_type": "Promotion" } }] } ], "region_definitions": [] } ``` ## JavaScript File The JavaScript file makes the content available for use with the template. The imported content’s fields are available in the `context.content.{id_from_definition}.attributes` object. Replace _{id_from_definition}_ with the value from the definition file’s `attribute_definition_groups.id` property. The template file uses a model object to access the content’s fields. To make the content’s fields accessible from the template, assign the content’s fields to the model. This sample JavaScript file corresponds to the definition file sample. In the definition file, the `attribute_definition_groups.id` value is `cmsPromotion`, so the content’s fields are accessible as properties of the `context.content.cmsPromotion.attributes` object. ```javascript "use strict"; var Template = require("dw/util/Template"); var HashMap = require("dw/util/HashMap"); /** Render logic for the assets.cmsPromotionBanner. */ module.exports.render = function (context) { var model = new HashMap(); var content = context.content; if (content.cmsPromotion) { model.keys = content.cmsPromotion.attributes; model.bannerTitle = content.cmsPromotion.attributes.Title; model.bannerCopy = content.cmsPromotion.attributes.detailText; model.bannerImageSrc = content.cmsPromotion.attributes["Banner Image"].absURL; model.bannerImageAlt = content.cmsPromotion.attributes["Banner Alternative Text"]; model.secondImageSrc = content.cmsPromotion.attributes["Second Image"].absURL; model.bannerImageAlt = content.cmsPromotion.attributes["Second Image Alternative Text"]; } model.callToAction = { title: content.cmsPromotion.attributes.callToActionText, link: content.cmsPromotion.attributes.callToActionUrl, }; return new Template("experience/components/assets/cmsPromotionBanner").render(model).text; }; ``` ## Template File The template file contains the HTML that determines how browsers display the component. In this file, use the `pdict` object to access the model for the content. All properties of the content model created in the JavaScript file are accessible as properties of the `pdict` object. This sample template file corresponds with the other sample files. ```html

``` ## See Also - [_B2C Commerce Cloud_: Develop a Component Type to Use CMS Content](https://documentation.b2c.commercecloud.salesforce.com/DOC1/topic/com.demandware.dochelp/content/b2c_commerce/topics/page_designer/b2c_cms_comp_type_banner.html) - [Display CMS Content in B2C Commerce Cloud](https://developer.salesforce.com/docs/platform/cms/guide/cms-dev-display-cms-content-in-b2c-commerce-cloud.md) # Before Using CMS Connect Review these pointers and prerequisites before you connect your CMS to Experience Builder. ## Your HTTP Server Must Serve HTML Fragments CMS Connect requires an HTTP server that can serve HTML fragments, either static or rendered on demand. Fragments can include headers, footers, components, CSS, or JavaScript. ## URLs in CSS and JavaScript Must Be Absolute URLs in CSS and JavaScript must be absolute. Relative URLs in HTML are OK and are converted for you. CMS Connect appends host names and converts relative URLs to absolute URLs in these HTML tags and attributes. - _\_ tag _src_ attribute - _\