Wolf X Machina

Back to blog

Implement OpenGraph for Facebook and Twitter Cards in DNN using 2sxc

Posted in DNN on February 20, 2021

I had a client recently ask me how to make "those nice links in Facebook" when sharing a URL. If you're not familiar yet, Facebook and Twitter and other social media sites and even iMessage use a protocol called "OpenGraph" to show really nice looking "cards" when you share a link. For example, if you have a webpage and you share the URL on Facebook, it doesn't just show the link, it typically displays content from the page in a card.

Knowing that DNN doesn't have a way for making links to webpages look good, I set about making a better experience for it. Read on.

If you're not already familiar with OpenGraph, here's a pretty good explanation of how OpenGraph works. So it's a pretty simple concept: you just put in some meta tags into the head of your document and the social media sites pick up on it and display it nicely in a card style. If you're like me and your website is built on DNN, you, as a developer, can easily enough just go to the Page Settings > Advanced tab > SEO sub-tab and then drop the meta tags into the Page Header Tags box. That's one way of doing it but there are a few problems and downsides:

  1. Your content editors probably don't want to mess around with HTML and copying and pasting meta tags
  2. If their OpenGraph "Description" is different from the Page's meta description, they would have to update both of these places to keep them in sync
  3. The editors would have to know the URL of the image and make sure it's sized correctly
  4. They can't preview what the card might look like without saving the settings and then going to https://developers.facebook.com/tools/debug/ and https://cards-dev.twitter.com/validator to check

For my purposes, with all my websites running on DNN and 2sxc, I wanted to make a better user experience that was more automated and gave the Content Editor a better experience. I know there are some OpenGraph modules out there but I don't like having to install yet another module for something when 2sxc can do it better. I care deeply about the content editor experience while also saving money and overhead on modules.

So here's what I did:

  1. I created a Content Type called "OpenGraph"
  2. My OpenGraph content types has only two fields: "Title" and "Image" (link)
  3. I created a razor template that the adds OpenGraph meta tags to the head of the page
  4. For each page, I drop this module on the page and then customize the Title and drag and drop an image

Typically you need a few things to set in OpenGraph. Here's what you need and an explanation of how I get / set them:

  1. site_name: @Dnn.Portal.PortalName
  2. type: website (hardcoded)
  3. description: @Dnn.Tab.Description
  4. image: set from the content field
  5. url: @dnnTab.FullUrl
  6. title: set from the content field title if it's set. If not set by the user, then I grab the title from the @Dnn.Tab.Title

Now you might be wondering, how do I put these tags into the head of the document when the module exists in the page? It's very simple - I use C# Razor that's already inside of 2sxc. Keep in mind, this template uses Bootstrap 4 and my own custom messaging so you'll have to just use this as a guide and equip it to your own website. Check out my template:

@inherits ToSic.Sxc.Dnn.RazorComponent
@using DotNetNuke.Entities.Tabs
@using ToSic.Razor.Blade;

@if (Content != null) {
    HtmlPage.AddOpenGraph("site_name", @Dnn.Portal.PortalName); 
    HtmlPage.AddOpenGraph("type", "website");
    HtmlPage.AddOpenGraph("description", @Dnn.Tab.Description);
    HtmlPage.AddOpenGraph("image", "https://" + @Dnn.Portal.PortalAlias.HTTPAlias.Replace("/en-ca","").Replace("/fr-ca","") + @Content.Image + "?mode=crop&h=601&w=589&anchor=middlecenter");
    HtmlPage.AddOpenGraph("url", @Dnn.Tab.FullUrl);

    if (Content.Title != "") {
        HtmlPage.AddOpenGraph("title", @Content.Title); 
    } else {
        HtmlPage.AddOpenGraph("title", @Dnn.Tab.Title);
    }     
    
}
    
   

@if(Edit.Enabled) {

<section class="py-5">
    <div class="container">
        <div class="row">
            <div class="col">
                <h2>Hey @Dnn.User.DisplayName! This is a hidden OpenGraph Card</h2>
                <div class="alert alert-info mb-4 mb-lc-0">
                    <p class="mb-0">If you're seeing this, it's because you're logged in with an administrator account and you can edit this. Regular users won't see this card - it's just for us to modify the OpenGraph so that when you share this page on Twitter or Facebook, the links will appear in a nice card style. This is not representative of a real Twitter or Facebook card but it's just to give you an idea of what might appear. You should always check <a href="https://cards-dev.twitter.com/validator" target="_blank">https://cards-dev.twitter.com/validator</a> and <a href="https://developers.facebook.com/tools/debug/" target="_blank">https://developers.facebook.com/tools/debug/</a> before posting. The description comes from the Page Settings > Description so modify that field to change the content. Keep in mind that it should be less than 200 characters.</p>
                </div>
                
                <div class="open-graph-card d-inline-flex mx-auto">
               <div class="row no-gutters rounded border shadow-lg align-items-center" @Edit.TagToolbar(Content, actions: "edit")>
                <div class="col-auto">
                    <img src="@Content.Image?mode=crop&h=125&w=125&anchor=middlecenter" />
                </div>
                   <div class="col px-2 text-smaller">
                       @if (Content.Title != "") {
                       <h5 class="mb-1 font-weight-bold">@Content.Title</h5>
                       } else {
                        <h5 class="mb-1 font-weight-bold">@Dnn.Tab.Title</h5>
                       }
                       <div class="line-clamp-3">
                        <p class="mb-1 text-smallest">@Dnn.Tab.Description</p>
                        </div>
                      
                        <p class="mb-0"><a href="@Dnn.Tab.FullUrl" class="stretched-link tet-decoration-none">@Dnn.Portal.PortalAlias.HTTPAlias.Replace("/en-ca","").Replace("/fr-ca","")</a></p>
                   </div>

                </div>
                </div>
            </div>
        </div>
    </div>
</section>   
    
<style type="text/css">
    .open-graph-card {
        width: 438px;
    }
    .line-clamp-3 {
        overflow: hidden;
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 3;
    }
    .text-smallest {
        font-size: 13px;
    }
</style>
    
}

So, let me explain how this works:

  1. On lines 1 through 18, I set up the template with some dependancies and then I use the 2sxc snippets to add the OpenGraph tags to the head section of the page.
    1. For most OpenGraph fields, I do the heavylifting for the user. Instead of forcing them to provide a description, I take the meta description right from the page settings. Same for the page URL and the portal name.
    2. If they don't put in a title, I just use the page title from the page settings
    3. I do make them upload an image but I use 2sxc's image resizer to make sure it conforms to the recommended size for OpenGraph cards
  2. Starting on line 22, I check if the user has "Edit" enabled (meaning they're a content editor who can edit the page). If they are, I display a nice, personalized message and a sample "card" of what it sort of looks like.
    1. I like to make the message really personalized and very evident that the module is not display publically even though it's published.
    2. It needs to be published so that the page gets the meta description but I also want to show the user some kind of UI that gives them a rough idea of what it's going to look like when they pop it into Facebook or Twitter or iMessage

The End Product

OpenGraph card in 2sxc

UPDATE: If you're seeing errors in the Twitter Card Validator or Facebook Debugger, make sure to edit your robots.txt file to allow them to crawl your website. Here's a gist to help with that.

I love love love these kind of solutions for many reasons:

  1. Cost and effort savings: I don't need to buy and install a new module that needs to be learned or maintained by my customers or I
  2. Reduction of content editor effort: My content editors don't need to learn how to use and manage HTML meta tags in the SEO Page Header Tags section, or even fill in most of those fields because my template does most of the work for them
  3. A module that evolves with the times: I can always modify or extend this module to change with the protocols as they change. Changing the template in one place updates it for all pages. No module re-installation / upgrading / patching needed
  4. As a developer, I'm not stuck by DNN's limitations: I've effectively gone around the problem of a feature that isn't present in DNN at this time.

So I've built a little, custom module in 2sxc that solves the problem exactly, makes the editing experience for my customer super simple, and costs less because I don't need another module.

When you start with the design to solve a problem, you find the code, and the overal solution, gets even better. Even if you don't end up using this for your customers, I hope you can take the idea of design-first to create amazing solutions.

Hope this helps! I build useful and valuable solutions like this all the time on websites. If this is something that strikes a chord with you, I would love to help you with your projects to make your websites more performant, more flexible, and better all around. Get in touch!

Aaron Lopez

Aaron Lopez

Founder & Lead Developer at Wolf X Machina

Wolf X Machina

Next Level Interface Development for the DNN CMS

DNN themes, module design, and more from Wolf X Machina.

Learn More