Motivation behind this
We know that from B2B Commerce Lightning we can use readymade Lightning Web Components available through B2B Lex and use it for many purposes like Product Detail Card, Product Detail List, Cart Component, Order Summary etc. at Experience Cloud.
Refer B2B Commerce on Lightning Experience Components and it also fetches data from CMS.
What if I have to build similar LWC component which can be placed in Lightning RecordDetail page for internal users and data to be retrieved from CMS. This drives me to do a proof-of-concept and sharing you the steps to develop.
Use Case
Business wants to see the list of featured products for internal users and those product images are placed at CMS.
Developer wants to build LWC component to fetch records from CMS.
For sake of simplicity, those images will be displayed using lightning-carousel component.
Solution Approach
To start with, lets configure CMS and store images on CMS side.
Create CMS Workspace
From CMS Home Tab, Create CMS Workspace. Here given a name as Capricorn Store. Screen as follows:
From Capricorn Store workspace, create a channel naming Capricorn Channel.
Add Content
Add 3 product related pictures as follows. For demo purpose I have used Coffee Machines pictures.
displayMediaFilesFromCMS.html
HTML will like below here lightning-carousel has been used.
<template> <lightning-card title="Featured Products">
<div class="slds-medium-size_1-of-4 slds-align_absolute-center"> <lightning-carousel>
<template for:each={results} for:item="item"> <lightning-carousel-image key={item.title} src= {item.url} header= {item.title} href= {item.url}> </lightning-carousel-image>
</template> </lightning-carousel>
</div> </lightning-card>
</template>
displayMediaFilesFromCMS.js
Few notable things as follows:
- Channel Name to be passed to Apex class's retrieveMediaFromCMS() method using wire. For sake of simplicity channel name is hardcoded, it can be captured from design attributes too and using @api variable.
- Loop through the array returned from Apex method and create new array with title and url as elements which will be used by carousel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import { LightningElement, track, wire } from 'lwc'; import retrieveMediaFromCMS from '@salesforce/apex/CMSConnectHelper.retrieveMediaFromCMS'; export default class DisplayMediaFilesFromCMS extends LightningElement { channelName = 'Capricorn Channel'; @track results=[]; @wire(retrieveMediaFromCMS,{channelName: '$channelName'}) wiredData({ error, data }) { if (data) { let objStr = JSON.parse(data); objStr.map(element=>{ this.results = [...this.results,{title:element.title, url:element.url}] }); this.error = undefined; } else if (error) { this.error = error; this.results = undefined; } } } |
CMSConnectHelper.cls
Salesforce provides ConnectApi.ManagedContent class and its method to retrieve data from CMS. No need to fetch data using REST APIs. Refer documentation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | /** * @description : This class is used to retrieve data from CMS * @author : Santanu Boral * **/ public with sharing class CMSConnectHelper { @AuraEnabled (cacheable=true) public static String retrieveMediaFromCMS(String channelName){ String channelId = getChannelId(channelName); //get the image content ConnectApi.ManagedContentVersionCollection obj = ConnectApi.ManagedContent.getAllContent(channelId, 0, 5, 'en_US', 'cms_image',false, '2011-02-25T18:24:31.000Z','2021-09-25T18:24:31.000Z',true); List<ReturnWrapper> wrapperList = new List<ReturnWrapper>(); System.debug('json value=' + JSON.serialize(obj)); //loop through each item and prepare a wrapper list for(ConnectApi.ManagedContentVersion versionObj: obj.items){ ReturnWrapper wrapper = new ReturnWrapper(); wrapper.title = versionObj.title; //get the url Map<String,ConnectApi.ManagedContentNodeValue> contentNodesMap = versionObj.contentNodes; for(String str:contentNodesMap.keySet()){ if(str=='source'){ wrapper.url= ((ConnectApi.ManagedContentMediaSourceNodeValue)contentNodesMap.get(str)).url; } } wrapperList.add(wrapper); } return JSON.serialize(wrapperList); } @AuraEnabled (cacheable=true) public static String getChannelId(String channelName){ ConnectApi.ManagedContentChannelCollection channelRepObj = ConnectApi.ManagedContent.getAllDeliveryChannels(0,2); //loop through the channels and return the channel Id for(ConnectApi.ManagedContentChannel channelObj: channelRepObj.channels){ if(channelObj.channelName == channelName){ return channelObj.channelId; } } return null; } public class ReturnWrapper{ String title {get;set;} String url {get;set;} } } |
getAllContent() returns a JSON as follows which needs to be parsed to form title and url combination for each item elements.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | { "currentPageUrl": "/services/data/v51.0/connect/cms/delivery/channels/0ap2v000000Pu1w/contents/query?endDate=2021-09-25T18%3A24%3A31.000Z&language=en_US&managedContentType=cms_image&page=0&pageSize=5&startDate=2011-02-25T18%3A24%3A31.000Z", "items": [ { "contentKey": "MCR6FJVMX4BRHSVJD4LPWUPPN3OI", "contentNodes": { "thumbUrl": { "nodeType": "Url", "value": "https://live.staticflickr.com/65535/49815666003_4973d84842.jpg" }, "title": { "nodeType": "NameField", "value": "TR-COFMAC-001" }, "source": { "fileName": null, "isExternal": true, "mediaType": "Image", "mimeType": null, "nodeType": "MediaSource", "referenceId": null, "resourceUrl": null, "unauthenticatedUrl": null, "url": "https://live.staticflickr.com/65535/49815666003_4973d84842.jpg" } }, "contentUrlName": "tr-cofmac-001", "language": "en_US", "managedContentId": "20Y2v000000HbISEA0", "publishedDate": "2021-05-09T16:32:44.000Z", "title": "TR-COFMAC-001", "type": "cms_image", "typeLabel": "Image", "unauthenticatedUrl": "https://myinstance.my.salesforce.com/cms/delivery/v51.0/0ap2v000000Pu1wAAC/contents/20Y2v000000HbISEA0?oid=00D2v000002A3VoEAK" }, { "contentKey": "MCSC6EEJVMZZB5HDHRKMTEQH6TIQ", "contentNodes": { "thumbUrl": { "nodeType": "Url", "value": "https://live.staticflickr.com/65535/49815637818_d892f8127d.jpg" }, "title": { "nodeType": "NameField", "value": "E-ESP-001-1" }, "source": { "fileName": null, "isExternal": true, "mediaType": "Image", "mimeType": null, "nodeType": "MediaSource", "referenceId": null, "resourceUrl": null, "unauthenticatedUrl": null, "url": "https://live.staticflickr.com/65535/49815637818_d892f8127d.jpg" } }, "contentUrlName": "e-esp-001-1", "language": "en_US", "managedContentId": "20Y2v000000HbIIEA0", "publishedDate": "2021-05-09T16:32:06.000Z", "title": "E-ESP-001-1", "type": "cms_image", "typeLabel": "Image", "unauthenticatedUrl": "https://myinstance.my.salesforce.com/cms/delivery/v51.0/0ap2v000000Pu1wAAC/contents/20Y2v000000HbIIEA0?oid=00D2v000002A3VoEAK" }, { "contentKey": "MCKPXLBGW36RDYXN75NDWFVTB5FA", "contentNodes": { "thumbUrl": { "nodeType": "Url", "value": "https://live.staticflickr.com/65535/49816090811_622af115a8.jpg" }, "title": { "nodeType": "NameField", "value": "B-C-COFMAC-001" }, "source": { "fileName": null, "isExternal": true, "mediaType": "Image", "mimeType": null, "nodeType": "MediaSource", "referenceId": null, "resourceUrl": null, "unauthenticatedUrl": null, "url": "https://live.staticflickr.com/65535/49816090811_622af115a8.jpg" } }, "contentUrlName": "b-c-cofmac-001", "language": "en_US", "managedContentId": "20Y2v000000HbIDEA0", "publishedDate": "2021-05-09T16:28:55.000Z", "title": "B-C-COFMAC-001", "type": "cms_image", "typeLabel": "Image", "unauthenticatedUrl": "https://myinstance.my.salesforce.com/cms/delivery/v51.0/0ap2v000000Pu1wAAC/contents/20Y2v000000HbIDEA0?oid=00D2v000002A3VoEAK" } ], "total": 3 } |
Finally, Create a Lightning App Builder page and place the component, it will look like this:
We are done and thanks for reading.
You can also extend this code to expose this component to Experience Cloud with some minor changes.
No comments:
Post a Comment