Adding Open Graph to Pelican

I just added Open Graph support to this Pelican-generated site so pretty previews would show up when people shared my posts, and I figured I'd share the fairly tidy way I went about it in case other people were interested. I only added support for articles, my journal entries, and pages, the other headers on this site, because adding it for things like tags didn't make a lot of sense (why would anyone share those?).

The changes were pretty simple. First, I added the following to the <head> section of template.html, the part of my custom theme from which every webpage on my site inherits its style:

{% if article is defined %}
<meta property="og:type" content="article" />
<meta property="og:url" content="{{ SITEURL }}/{{ article.url }}" />
<meta property="og:title" content="{{ article.title | replace("\"", "&quot;") }}" />
<meta property="og:description" content="{{ article.content | striptags | replace("\"", "&quot;") | truncate(196, False, '...') }}" />
<meta property="og:image" content="{{ SITEURL }}/images/{% if article.opengraph_image is defined %}{{ article.opengraph_image }}{% else %}opengraph-default.jpg{% endif %}" />
{% elif page is defined %}
<meta property="og:type" content="website" />
<meta property="og:url" content="{{ SITEURL }}/{{ page.url }}" />
<meta property="og:title" content="{{ SITENAME }} - {{ page.title | replace("\"", "&quot;") }}" />
<meta property="og:description" content="{{ page.content | striptags | replace("\"", "&quot;") | truncate(196, False, '...') }}" />
<meta property="og:image" content="{{ SITEURL }}/images/{% if page.opengraph_image is defined %}{{ page.opengraph_image }}{% else %}opengraph-default.jpg{% endif %}" />
{% else %}
<meta property="og:type" content="website" />
<meta property="og:url" content="{{ SITEURL }}/{{ output_file }}" />
<meta property="og:title" content="{{ SITENAME }}" />
<meta property="og:description" content="A page on Liz Denys's personal site" />
<meta property="og:image" content="{{ SITEURL }}/images/opengraph-default.jpg" />
{% endif %}

It was obvious to treat what Pelican calls articles, my journal/blog entries, as articles, and I decided to treat my Pelican pages, basic information about me, as websites. My article titles stand well on their own, but my page titles are accompanied by Liz Denys, my SITENAME, because they don't make as much sense on their own. Everything else - my journal's timeline and tag pages - doesn't have a lot of information in Pelican, so they just got a boring default.

Raw double quotes would be problematic in meta tags, so I make sure to replace them with the friendlier HTML entity &quot; in both the titles and descriptions with Jinja's replace function. I've found that double quotes in article titles mess other things up in Pelican, but still applied the replace to titles here just in case that changes.

The Open Graph description property, og:description, is generally a preview of the website's content, and since I don't use Pelican's summary attributes in my theme, I opted to grab a bit of the content directly with article.content or page.content. I couldn't find a definitive answer as to how much that should be - Open Graph says a description is "a one to two sentence description of your object," Facebook defines a description as "a clear description, at least two sentences long," and Twitter specifies that a description has a maximum of 200 characters. These seemed a bit conflicting, so I followed the clearest instruction, Twitter's, and limited my descriptions to 200 characters, including the possible trailing " ..." with Jinja's truncate function.

Some, but not all, of my articles and pages contain images, so I wanted a default og:image for the entire site that could get overridden on webpages where something specific made more sense. I briefly considered progamatically taking the first image to appear in a post for its preview, but when I looked through some older posts, I noticed this wasn't always the right choice both in terms of content and size - you need at least a 200px square for Facebook and probably want even larger. I uploaded a default og:image to "{{ SITEURL }}/images/opengraph-default.jpg". I named the file that instead of something that describes its content (currently an old photo of me in SIPB's machine room) so that I would only need to upload a new image to the same location to change it later. When I want to override this default for an article or page, I'd simply define opengraph_image in the meta information of its Markdown file like so:

Title: Inbox by Gmail's accidentally abusive algorithm
Slug: inboxs-accidentally-abusive-algorithm
Date: 2016-05-14 11:03
Tags: a little too helpful; content algorithms; email; gmail; Google; harassment; inbox; product design
opengraph_image: software/inboxs-accidentally-abusive-algorithm/inbox-speed-dial.jpg

The code in the template checks whether or not this meta information exists, so if I want to use the site default, I simply leave it out.

Once I added this to my site, I checked that my new Open Graph information rendered as I expected with Facebook's Sharing Debugger and Twitter's Card Validator. I also sent direct messages containing links to my posts on Slack to see how they rendered there with the new Open Graph information. Here's what one of my articles now looks like when someone posts it to Facebook:

Facebook share preview for Inbox by Gmail's speed dial with Open Graph information including an image, title, and description

That same article when it's shared on Twitter:

Twitter card preview for Inbox by Gmail's speed dial with Open Graph information including an image, title, and description

And finally, what it looks like when posted to Slack:

Slack preview for Inbox by Gmail's speed dial with Open Graph information including an image, title, and description