Adding Open Graph Fields

The default SEO UI on a Content Edit Page does not provide fields for users to enter Open Graph tags on content-carrying objects. You must create a modification class that adds Open Graph-specific fields to the SEO tab (or any other tab). In addition, you must update the frontend and ViewModel source to render the Open Graph metadata on web pages.

The following steps show how to add Open Graph fields to the SEO tab for an Article class. While the user-entered data from these fields are stored in Article objects, the Open Graph <meta> tags are not rendered by the article ViewModel. They are rendered by the page ViewModel that generates the HTML <head> element, the container for Open Graph <meta> tags. See Open Graph Properties for an example of rendered Open Graph meta tags.

Step 1: Create a Modification

The following example shows a modification of the Article class. The modification adds three Open Graph fields to the SEO tab, allowing users to set the content attribute on these Open Graph properties. (Additional Open Graph properties will be auto-generated on the frontend.) As specified by the @Recordable.FieldInternalNamePrefix annotation, the Open Graph fields will be identified internally with the cms.og prefix.

@Recordable.InternalNamePrefix("cms.og.")
public class OpenGraphData extends Modification<Article>
{
   @ToolUi.Tab("SEO")
   @Recordable.DisplayName("Open Graph Title")
   private String ogTitle;
   @ToolUi.Tab("SEO")
   @Recordable.DisplayName("Open Graph Description")
   private String ogDescription;

   /* This field is used with the Open Graph object type, "og:type".
      It stores tag words associated with Article objects. */
   @ToolUi.Tab("SEO")
   @Recordable.DisplayName("Open Graph Tags")
   private List<String> ogTags;

   // Getters and Setters
}

As a result of the modification, fields are added to the SEO UI, allowing users to add Open Graph content:

../../../_images/seo-tab-updated.png

Step 2: Add Handlebars Placeholders

In the View’s Handlebars template for the page, add the Open Graph <meta> tags.

 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
 <!DOCTYPE html>
 <html>

 <head>
    <meta charset="UTF-8">
    <title>{{title}}</title>
    <link href="/styleguide/All.min.css" type="text/css" rel="stylesheet"/>
    <script src="/styleguide/All.min.js" type="text/javascript"></script>
    <meta property="og:title" content="{{ogTitle}}" />
    <meta property="og:description" content="{{ogDescription}}" />
    <meta property="og:type" content="article:tag" />
    <meta property="article:tag" content="{{ogTags}}" />
    <meta property="og:image" content="{{leadImageUrl}}" />
    <meta property="og:url" content="{{ogUrl}}" />
</head>

<body>
    <header class="Page-header">{{title}}</header>

    <div class="Page-body">
        {{body}}
    </div>
</body>

</html>

In the previous snippet—

  • Lines 9, 10, 12 include placeholders for values that are entered in the SEO tab.
  • Line 13 includes a placeholder for a URL of an image set in the main Article tab.
  • Line 14 includes a placeholder for a URL of the published page. This value is retrieved from the page ViewModel (below).

Step 3: Add JSON Keys

In the View’s JSON file for the page, add key-value pairs for the Open Graph properties.

1
2
3
4
5
6
7
8
{
   "_template": "Page.hbs",
   "ogTitle": "{{words(10)}}",
   "ogDescription": "{{words(25)}}",
   "ogTags": ["{{words}}"],
   "leadImageUrl": "{{image(400, 300)}}",
   "ogUrl": "{{words}}"
}

In the previous snippet—

  • Lines 3–5 show keys added for Open Graph property data that are entered in the SEO tab..
  • Line 6 is the key for the URL of the image set in the main Article tab.
  • Line 7 is the key for the URL of the published page.

Step 4: Add Methods to the ViewModel

In the PageViewModel class, add methods to populate the Open Graph properties.

public class PageViewModel extends ViewModel<Content> implements PageView, PageEntryView {

   @Override
   public CharSequence getTitle() {
      return model.as(Seo.ObjectModification.class).findTitle();
   }

    @Override
    public Iterable<? extends PageViewBodyField> getBody() {
      return createViews(PageViewBodyField.class, model);
   }

   // Gets ogTitle value from OpenGraphData modification of Article.class
   @Override
   public CharSequence getOgTitle() {
     return model.as(OpenGraphData.class).getOgTitle();
   }

   // Gets ogDescription from OpenGraphData modification of Article.class
   @Override
   public CharSequence getOgDescription(){
     return model.as(OpenGraphData.class).getOgDescription();
   }

   // Gets ogTags from OpenGraphData modification of Article.class
   @Override
   public List<String> getOgTags() {
     return model.as(OpenGraphData.class).getOgTags();
   }

   // Gets page URL from permanent link generated for the Article object
   @Override
   public CharSequence getOgUrl() {
     return model.as(Article.class).getPermalink();
   }

   // Gets URL of image set on the Article object
   @Override
   public CharSequence getLeadImageUrl() {
     Image leadImage = model.as(Article.class).getLeadImage();
     if (leadImage != null) {
         StorageItem file = leadImage.getFile();

         if (file != null) {
             return file.getPublicUrl();
         }
     }
     return null;
   }
}