Forum Discussion

Claudius's avatar
10 months ago

How to add ProfilePage Structured Data markup

Google just announced the availability of a new Profile Page structured data markup. This is intended to make Profile data of creators (like community members) available for richer search results and insight. Alongside the announcement they also added support for testing this new markup in Google Search Console.

Here's the Freemarker code to create JSON linked data payload to be added to a Khoros Classic community's View Profile Page 

 

<#-- This component will add structured markup in form of JSON-LD. To be placed anywhere within ViewProfilePage -->

<#if page.name == 'ViewProfilePage'>

    <#assign userInfoQuery = "select kudos_received.sum(weight), kudos_given.sum(weight), messages.count(*), rank.name from users where id = '${page.context.user.id}'"/>
  	<#assign userInfo = rest("2.0", "/search?q=" + userInfoQuery?url("UTF-8") + "&restapi.response_style=view").data.items />
 <script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "ProfilePage",
      "dateCreated": "${page.context.user.registrationTime}",
      "mainEntity": {
        "@type": "Person",
        "name": "${page.context.user.login}",
        "identifier": "${page.context.user.id}",
        "agentInteractionStatistic": [{
          "@type": "InteractionCounter",
          "interactionType": "https://schema.org/LikeAction",
          "userInteractionCount": ${userInfo[0].kudos_given.sum.weight}
        },{
          "@type": "InteractionCounter",
          "interactionType": "https://schema.org/WriteAction",
          "userInteractionCount": ${userInfo[0].messages.count}
        }],
        "description": "${userInfo[0].rank.name}",
        "image": "${page.context.user.avatar.sizes.name.print.url}"
      }
    }
    </script>
</#if>

 

I've created a dedicated component so I can add it to the ViewProfilePage layout as JSON-LD script payload can go anywhere.

Some decision I took to map Khoros profile data:

  1. User registration timestamp gets mapped to entity dateCreated property
  2. Khoros user id becomes the identifier
  3. There doesn't seem to be an equivalent to "likes received" in the ProfilePage spec, so I only mapped the "Kudos given" profile metric to the LikeAction property
  4. I'm  using the user's rank name to the description property since user bio gets populated super rarely. That might be different in your community so you could change that.

I hope this code is useful and would love to hear if you think my interpretation of the ProfilePage spec fits. Please share back if you are making improvements to the script.

  • Cheers, literally just put a ticket in the work stack this morning to look at the new markup.

  • A colleague told me about this update yesterday so I signed in to Atlas just now to learn a bit more and boom, here you are with the solution.

    You're a legend Claudius, thank you for this! 
     

  • A little note in regards to what Claudius mentioned above:

    I'm using the user's rank name to the description property since user bio gets populated super rarely. That might be different in your community so you could change that.

    If you do use the bio, make sure to use the FreeMarker built-in ?json_string because the bio can contain double quotes which will break the json and cause errors when Search-Crawlers are trying to parse the structured data.