Forum Discussion

Parvez_AL's avatar
Parvez_AL
Advisor
5 months ago

Link preview in Teams shows blank image

When we share a link for our Community topic or blog article in MS Teams, the preview is always blank photo. How do we make sure there is a thumbnail preview when sharing a link. Is there any setting in Community Admin that we can update.  

  • As a starter before the very useful coding snippets from Drew and Andrew you might want to start populating the "Open Graph" node image URL on your community root to point to a community image within your assets. The image should be at least 1200 pixels wide. A good ratio is 1.91:1 as that's what Facebook as the founder of Open Graph started with.

    You can find that setting in Admin -> Features-> Open Graph 

    A nice visual way to validate the setting is via https://opengraph.dev/

  • I think I fixed this in Studio -> Community Style -> /skin -> Page Head Top Content by adding meta info for the Twitter card and image. It just displays our Community logo.

    https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/summary

    <meta name="twitter:card" content="summary" />
    <meta name="twitter:image" content="${asset.get("/html/assets/company-community-logo.png")}" />
    <meta property="og:image" content="${asset.get("/html/assets/company-community-logo.png")}" />
    <meta property="og:image:alt" content="Company Community Logo" />

     I'll look again, but this should at least get something to display for you.

  • Parvez_AL Drew_C 

     

    We got so frustrated with the out of the box open graph implementation we ended up working with professional services to roll our own.

    Happy to share the code but be aware you will probably need professional services to help you role it, our code also uses some custom fields you probably do not have.

    <#import "common-functions.ftl" as utility />
    <#import "theme-lib.utility-macros.ftl" as theme />
    
    <#--Set default values -->
    <#assign ogType = "" />
    <#assign ogImage = coreNode.settings.name.get('custom.placeholder_image','') />
    <#assign boardImg = "" />
    <#assign siteName = coreNode.settings.name.get('seo.custom_og_site_name','') />
    <#assign largeCards = coreNode.settings.name.get('custom.twittersummarycards','false') />
    <#assign teaserImg = "" /> 
    <#assign bodyImg = "" /> 
    <#assign coverImg = "" /> 
    <#assign boardImg = "" />
    <#assign isLargeImg = false/>
    
    
    <#-- create page type map -->
    <#assign websitePages = ["CommunityPage", "CategoryPage", "ForumPage", "BlogPage", "IdeaExchangePage", "OccasionBoardPage", "TkbPage" ] />
    <#assign articlePages = ["OccasionPage", "IdeaPage", "BlogArticlePage", "ForumTopicPage", "TkbArticlePage"] />
    <#assign profilePages = ["ViewProfilePage"] />
    
    <#-- Identify Type of page -->
    <#if websitePages?seq_contains(page.name)>
        <#assign ogType = "website" />
    <#elseif articlePages?seq_contains(page.name)>
        <#assign ogType = "article" />
        <#assign authorUrl= webuisupport.urls.fullyQualifiedPage.name.get("ViewProfilePage").path("user-id","${(page.context.thread.topicMessage.author.id)!''}").build()/>
        <meta content="${authorUrl}" property="article:author">
    <#elseif profilePages?seq_contains(page.name)>
        <#assign ogType = "profile" />
    <#else>
        <#assign ogType = "other" />
    </#if>
    
    
    <#-- asign value to fields based on page type -->
    <#if ogType == "website">
    	<#assign ogURL = coreNode.webUi.url/>
    	<#assign ogTitle = coreNode.title/>
      	<#assign description = coreNode.description />
    <#elseif ogType == "article">
        <#assign msgID = page.context.thread.topicMessage.uniqueId!'0'/>
        <#assign msgID = page.context.thread.topicMessage.uniqueId!'0'/>
      	<#assign liqlQuery = "select subject,teaser,body,canonical_url from messages where id='${msgID}'" />
    	<#assign msgData = utility.executeLiQLQuery(liqlQuery)[0] />
    	<#assign msgTeaser = utility.smartTruncateString(theme.liRemoveHTML(msgData.teaser),160,"..")!"" />
    	<#assign msgBody = utility.smartTruncateString(theme.liRemoveHTML(msgData.body),160,"..")!"" />
      	<#assign msgSubject = msgData.subject />
    	<#assign msgCanonURL = msgData.canonical_url!"" />
      	<#if msgCanonURL?has_content>
        	<#assign ogURL = msgCanonURL />
        <#else>
    	   <#assign ogURL = http.request.url?html/>
        </#if>
    	<#assign ogTitle = msgSubject?html/>
    	<#if msgTeaser?has_content>
    		<#assign description = msgTeaser/>
    	<#else>
    		<#assign description = msgBody/>
    	</#if>
    <#elseif ogType == "profile">
    	<#assign ogURL = http.request.url?html />
    	<#assign ogTitle = (page.context.user.login)!'' />
        <#assign description = coreNode.description />
    <#else>
        <#assign ogURL = http.request.url?html/>
    	<#assign ogTitle = page.name/>
        <#assign description = coreNode.description />
    </#if>
    <#assign description = theme.liRemoveHTML(description)/>
    
        
    
        <#attempt>
            <#--  custom.twittersummarycards is the SLE used for social media large summary cards
                remove custom.twitter_summary_cards-->
    
    <#-- depending on the type of page choose the OG image to use -->
            <#if page.name == 'BlogArticlePage'>
                <#if (coreNode.id == "Video-Hub")>
                    <#assign videoHubMessageId =  page.context.thread.topicMessage.uniqueId />
                    <#assign videoHubMessageQuery = "SELECT c_video_thumb_image, board.c_placeholder_image FROM messages WHERE id='${videoHubMessageId}'"/>
                    <#assign videoHubMessages = utility.executeLiQLQuery(videoHubMessageQuery) />
                    <#if (videoHubMessages!?size gt 0)>
                        <#assign videoThumpImageUrl = videoHubMessages[0].c_video_thumb_image!"" />
                        <#if (videoThumpImageUrl!?length gt 0)>
                        <#assign ogImage = videoThumpImageUrl />
                        <#else>
                            <#assign ogImage = (videoHubMessages[0].board.c_placeholder_image)!'' />
                        </#if>
                    </#if>
                <#else>
                    <#assign topicMessageId = page.context.thread.topicMessage.uniqueId />
                    <#assign liql_query =
                        {
                            "images":{
                                "fields":["association_type","original_href", "messages"],
                                "constraints":[{"messages.id": "${topicMessageId}"}],
                                "subQueries":{
                                    "messages":{
                                        "fields":["board.c_placeholder_image", "board.c_twittersummarycards"]
                                    }
                                }
                            }
                        }
                    />
                    <#assign msgImgs = restBuilder().method("POST").path("/search").body(liql_query).call().data.items/>
    
    
                    <#if msgImgs?has_content>
                        <#list msgImgs as msgImg>
                            <#assign isLargeImg = (msgImg.messages.items[0].board.c_twittersummarycards)!false>
                            <#if msgImg.association_type?has_content>
                                <#switch msgImg.association_type>
                                    <#case "teaser">
                                        <#if !teaserImg?has_content>
                                                <#assign teaserImg = msgImg.original_href /> 
                                        </#if>
                                        <#break>
                                    <#case "body">
                                        <#if !bodyImg?has_content >
                                            <#assign bodyImg = msgImg.original_href /> 
                                        </#if>
                                        <#break>
                                    <#case "cover">
                                        <#if !coverImg?has_content>
    										<#assign coverImg = msgImg.original_href /> 
                                        </#if>
                                        <#break>
                                </#switch>
                            </#if>
                            <#if !boardImg?has_content>
                                <#--  <#assign boardImg = msgImg.messages.items[0].board.c_placeholder_image />  -->
                                <#assign boardImg = coreNode.settings.name.get('custom.placeholder_image','') />
    
                            </#if>
                        </#list>
                        <#if teaserImg?has_content>
                            ${utils.logs.name.open_graph.info("====================== teaserImg has_content ==========================")}
                            <#assign ogImage = teaserImg />
                        <#elseif bodyImg?has_content>
                            ${utils.logs.name.open_graph.info("====================== bodyImg has_content ==========================")}
                            <#assign ogImage = bodyImg />
                        <#elseif coverImg?has_content>
                            ${utils.logs.name.open_graph.info("====================== coverImg has_content ==========================")}
                            <#assign ogImage = coverImg />
                        <#elseif boardImg?has_content>
                            ${utils.logs.name.open_graph.info("====================== boardImg has_content ==========================")}
                            <#assign ogImage = boardImg />
                        </#if>
                    <#else>
                        <#assign ogImage =  coreNode.settings.name.get('custom.placeholder_image','')/>
                    </#if>
                </#if>
            <#elseif page.name == 'ForumTopicPage' || page.name == 'OccasionPage' || page.name == 'IdeaPage' || page.name == 'TkbArticlePage'>
                <#assign topicMessageId = page.context.thread.topicMessage.uniqueId />
                <#if (page.name == 'OccasionPage')>
                    <#assign bannerImg = '' />
                    <#assign liql_query =
                        {
                            "images":{
                                "fields":["association_type","original_href", "messages"],
                                "constraints":[{"messages.id": "${topicMessageId}"}],
                                "subQueries":{
                                    "messages":{
                                        "fields":["board.c_placeholder_image", "c_twittersummarycards"]
                                    }
                                },
                                "limit": 1
                            }
                        }
                    />
                <#else>
                    <#assign liql_query =
                        {
                            "images":{
                                "fields":["original_href" "messages"],
                                "constraints":[{"messages.id": "${topicMessageId}","association_type":"body" }],
                                "subQueries":{
                                    "messages":{
                                        "fields":["board.c_placeholder_image", "c_twittersummarycards"]
                                    }
                                },
                                "limit": 1
                            }
                        }
                    />
                </#if>
                
                <#if (page.name == 'OccasionPage')>
    
                    <#assign bodyImg = "" /> 
                    <#assign coverImg = "" />
                    <#assign boardImg = ogImage />
    
                    <#assign msgImgs = (restBuilder().method("POST").path("/search").body(liql_query).call().data.items)![]/>
                    <#list msgImgs as msgImg>
                        <#if msgImg.association_type?has_content>
                            <#switch msgImg.association_type>
                                <#case "body">
                                    <#if !bodyImg?has_content >
                                        <#assign bodyImg = msgImg.original_href /> 
                                    </#if>
                                    <#break>
                                <#case "cover">
                                    <#if !coverImg?has_content>
    									<#assign coverImg = msgImg.original_href /> 
                                    </#if>
                                <#break>
                             </#switch>
                        </#if>
                    </#list>
                    <#if bodyImg?has_content>
                        <#assign ogImage = bodyImg />
                    <#elseif coverImg?has_content>
                        <#assign ogImage = coverImg />
                    <#elseif boardImg?has_content>
                        <#assign ogImage = boardImg />
                    </#if>
                <#else>
                    <#attempt>
                        <#assign msgImg = (restBuilder().method("POST").path("/search").body(liql_query).call().data.items[0])!{}/>
                        <#assign boardImg = (msgImg.messages.items[0].board.c_placeholder_image)!'' />
                    <#recover>
                        <#assign msgImg = {} />
                        <#assign boardImg = '' />
                    </#attempt>
                    <#if msgImg?has_content>
                        <#assign ogImage = msgImg.original_href />
                    </#if>
                </#if>
            </#if>
    		<#assign metaOGImage = "${ogImage!boardImg}"/>
    		
    		<#--- OpenGraph Meta Data -->
    		<#--  og:site_name  -->
    		<#if siteName?has_content>
    			<meta content="${siteName}" property="og:site_name"/>
    		</#if>
    		<#--  og:type  -->
    		<meta content="${ogType}" property="og:type"/>
    		<#--  og:title  -->
    		<meta content="${(ogTitle)!''}" property="og:title"/>
    		<#--  og:description  -->
    		<meta content="${(description)!''}" property="og:description"/>
    		<#--  og:url  -->
    		<meta content="${ogURL!''}" property="og:url"/>
    		<#-- og:image -->
            <#if metaOGImage?has_content>
    			<#if metaOGImage?starts_with("/")>
    				<#assign metaOGImage = webuisupport.urls.getBuilder("${metaOGImage}").build()/>
    			</#if>
                <meta name="image" content="${metaOGImage}" property="og:image"/>
            </#if>
    		
    		<#-- Twitter / X Data -->
    		<#-- meta twitter card sizes =-->
    		<#-- Large summary cards or normal summary cards -->
    		<#if largeCards == 'false' >
    			<meta name="twitter:card" content="summary"/>
    		<#else>
    			<meta name="twitter:card" content="summary_large_image"/>
    		</#if>
    		<#-- Twiter site name -->
    		<meta name="twitter:site" content="${text.format('custom.twitter.handle')}"/>
    		<#-- Twitter Site description -->
    		<meta name="twitter:description" content="${description!''}"/>
    		<#--twitter site title -->
    		<meta name="twitter:title" content="${ogTitle!''}"/>
    		<#if metaOGImage?has_content>
    			<#-- Tiwtter site Image -->
    			<meta name="twitter:image" content="${metaOGImage}" />
    		</#if>
        <#recover>
        </#attempt>

     

    This is a root canal for opengraph and is probably far to involved for your original question.