Ant Smith

Articles

Website Integration of facebook Social Plug-ins

    In this article I look at

  • Allowing cross-site scripts to execute to enable social plug-ins

  • Managing URLs, so plug-ins know what to do

  • Adding metadata for administration of plug-in content

  • Adding the plug-in code to a page

This entire discussion concerns creating web pages with PHP (in fact PHP version 7.3.20). You might find this useful even if you're creating pages in some other way, or not, I don't really know – YOU DECIDE!

    My website provides 3 facebook integrations:

  • The 'Like' button and counter (top of page)

  • 'Share' the page (in the Share Box near the bottom of the page)

  • 'Comment' on the page (under the Share Box)

The trouble with this world today is that everything seems to be somebody else's problem.

curmudgeon, 2020

In principle the facebook social plug-ins are pretty straightforward, leastways that's how it seems when you read the documentation.. They even have configuration tools in the documentation that automagically creates all the code you need – hummm, except it doesn't.

Well, it does if you've already set-up a facebook developer account. And created an Application Identifier. And added the special Metadata tags needed to your page; And created the security headers that allows facebook scripts to run on your own website...

Sure you can search on-line to find out how to do all of the foregoing; once you realise you need to do all of the foregoing; but there doesn't seem to be many (or any) one-stop references covering the whole story. So I thought I'd jot down what I've discovered and implemented on my own website, in case it might be useful; even if it's not.

Enabling Social Plug-Ins

In essence a social plug-in is a small section in a page on your website that executes code that resides on the service provider's website; I.e code that you have no real control over. It might well do what you want (allow comments, likes etc...) but it might do other stuff too (track the behaviour of your website visitors) – which might just be 'the cost of entry'; or it might be something more malicious.

If you want to integrate facebook into your website there's nothing much you can do about their shitty attitude towards data harvesting; other than suck-it-up. It's just the way of the world. BUT because executing code from a domain other than your own website's domain is so very dangerous, in general these days web browsers don't permit it. By default they simply block scripts from other places. So you have to explicitly state that you're okay with scripts running from certain specified (they call it 'trusted') sources.

Now, of course, we don't trust the likes of facebook; but if we want to use their social plug-ins we have to suck-it-up and pretend like we do.

To do that we have to send Response Headers - these are little statements we send to the website visitor's browser before we send the actual webpage. Statements of things we'd like the browser to do – such as allowing scripts from so called 'trusted' domains to execute.

Headers have to be sent before any part of the HTML page. It's best to send them as early as possible in the code that creates the webpage. Sometimes including a file will inadvertently send a blank line (if there happens to be a blank line at the end of the included file). So you can find output has been sent without your realising it. So set the headers before you do anything else.

In PHP they are sent using the header function.

Here's the headers I've used to allow facebook scripts to run on my website domain:

header ('Content-Security-Policy: frame-ancestors https://www.facebook.com/');

header ('X-Frame-Options: ALLOW-FROM https://www.facebook.com/');

Like everything in the jumbled mess that is the t'Internet you can see some redundancy here – I have to ask the browser twice to allow scripts from facebook. This is because Content Security Policy headers are somewhat new and a lot of older browsers that people are still using will just ignore them.

Managing URLs, Meta Tags, and data-href tag attributes

You might have thought that because the social plug-ins are actually on your webpage that they would know where that webpage is – what it's URL is. But they don't, it's almost like they're blindfolded hostages held in the basement of some indeterminate building.

The fact is, the URL of the page and the actual address (in the browser's address bar, or in the DOM's location attribute) can be very different things.

For example, I have a URL /articles/cameras-of-my-life – and that's usually what you see in the address bar when visiting the article on my website. However, after sharing this page to facebook, when I click on the facebook link and arrive at the article on my website the browser address shows:

http://antsmith.net/articles/cameras-of-my-life?fbclid=IwAR0kgxRLZ7wC7AmC8wlDr06-T_0OSpS6Y9qAxwDs1Pi8V7BbRbuQ4flDr6U

So the facebook social plug-in can't simply rely on the address of the page it finds itself on (in this case because facebook as messed about with the URL).

Actually, believe it or not, things are a tad more complicated. The URL I mentioned above shows this article as one of 50 or so that I have on the site. These articles are also organised by 'tag'. The above article can also be read in the context of 'articles about photography' – via the URL /articles/cameras-of-my-life/tag/photography; which is a different address even though it is essentially the same article.

To get around these issues there's a range of Metadata Tags and HTML Tag Attributes on every page in my website that provide the 'true' URL of the page, vis-á-vis:

1. <link rel = "canonical" href="http://antsmith.net/articles/cameras-of-my-life" >
2. <meta property="og:url" content="http://antsmith.net/articles/cameras-of-my-life/tag/photography" />
3. <div class="fb-like" data-href="http://antsmith.net/articles/cameras-of-my-life"...>
4. <a target="_blank" rel="nofollow" href="https://www.facebook.com/sharer.php?u=/articles/cameras-of-my-life/tag/photography"...>
5. <div class="fb-comments" data-href="http://antsmith.net/articles/cameras-of-my-life/tag/photography"...>

Here the first item (the canonical link) ensures that the likes of Google et al. understands that 'cameras-of-my-life/tag/photography' is the same essential content as 'cameras-of-my-life'; the two pages share the same canonical link. This ensures we don't get duplicated results from search engines.

And note, the third item above (the facebook like button) also takes the canonical version of the page in its data-href attribute. This ensures that any 'likes' of the article on either the base article page or the tagged article page will show up on both pages. Which is how it should be.

UPDATE 19.10.2020: Oh no it doesn't..! This stopped working a couple of days ago so I went back to the documentation to see (now) it says og:url and data-href must be the same; so no more aggregating likes around canonical and non-canonical versions. It's very frustrating. I wish you'd all stop using facebook...

The fifth item above (the comments plug-in) also has a data-href attribute. This time though it takes the 'actual' (ie. tagged version) of the URL. This means that comments on the tagged version of the page do not show up on the base article, and vice versa. Comments do not aggregate in the same way that Likes do. Personally I don't think facebook have (bothered) to get this right. Comments ought to aggregate on canonical and non-canonical versions of the same page. I could make that happen by using the canonical URL for the comments data-href - but if I do things get weird when a comment is 'also posted to facebook'; the link that gets shared with the posted comment is then to the canonical version of the page, not the tagged version that the poster was on when commenting. Oh well, it's not the worst thing about facebook.

The fourth item above (share) shares the actual (tagged) version of the page – as it should, since that's the version the person sharing is seeing.

Finally, the second item (og:url) is the URL that facebook uses when the page is shared in any other way – ie. when putting the page directly into a post from within facebook, rather than using the page's 'share' button. If the right thing is to be shared this has to take the actual (tagged version) of the page's URL, not the canonical URL. BTW this is what the comments plug-in ought to use when 'also posting to facebook' – if it did we could then use the canonical URL for the comments plug-in and get comments aggregated in the same was as we get 'likes' aggregated. But that would mean the social plug-in team at facebook would have to realise that facebook also has this tag available, but apparently they don't realise that...

URL and Tag Summary

The address bar (the document location) isn't necessarily a clean version of the page's URL – services like facebook might appended crappy 'URL parameters' to it.

When making the page two forms of URL have to be constructed. The actual, clean, URL of the page (with crappy extra parameters stripped from it) and the canonical version of the URL. These might or might not be the same thing.

I use the following PHP code to create a clean version of the page's URL:

function cleanURL () {
	if (!isset($_SERVER['REQUEST_URI'])) {
		$serverrequri = $_SERVER['PHP_SELF'];
	} else {
		$serverrequri = $_SERVER['REQUEST_URI'];
	}
	$s = empty($_SERVER["HTTPS"]) ? '' : ($_SERVER["HTTPS"] == "on") ? "s" : "";
	$protocol = strleft(strtolower($_SERVER["SERVER_PROTOCOL"]), "/").$s;
	$port="";
	$port = ($_SERVER["SERVER_PORT"] == "80") ? "" : (":".$_SERVER["SERVER_PORT"]);
	$thisURL=$protocol."://".$_SERVER['HTTP_HOST'].$port.$serverrequri;

	$thisURL = explode ('?',$thisURL)[0];

	return $thisURL;
}

And then the following code (suitable for my website) creates the canonical version of a clean URL:

function canonicalURL () {
	$thisURL = cleanURL();
	//need to find first collection/tag item and make those the canonical if no item specified
	//this need is handed-off to the itempage
	//similarly special canonical handling of slideshows is handed off to the photolist
	//so here we only remove the parts of the URL that add context
	$thisURL = explode ('/tag/',$thisURL)[0];
	$thisURL = explode ('/collection/',$thisURL)[0];
	$thisURL = trim ($thisURL,'/');
	return $thisURL;
}

The page should have a canonical link tag (<link rel=”canonical”...>) that uses the canonical URL for the page.

The facebook like button's data-href should also use the canonical version of the URL (so that likes properly aggregate across canonical and non-canonical versions of the content.

facebook's meta tag og:url and the URL provided to the share function (share.php?u=...) must be the clean URL.

facebook's data-href attribute of the comments plug-in has to be the clean URL – even though if facebook really knew what they were doing it would be the canonical URL. But they don't. So it isn't.

Additional Meta Tags

For the social plug-ins to do their work, you also have to provide a facebook app id for some godforsaken reason; it certainly has nothing to do with making the integration easy. Look, if I was some dodgy capitalist start-up company sniffing about inside the graph api looking to get my fingers on all that juicy user data facebook likes to sell out then yeah, make me register and request an app id; but it really shouldn't be necessary if all I want is to let other people use their facebook log-ins to like, share and comment on my pages. It shouldn't be necessary, but it is. Because facebook are crappy and they get things wrong.

To claim an app id log-in to facebook then visit https://developers.facebook.com. Press the get started menu option then click next on the pop-up before properly reading it. Then wonder if you should have read it and curse when you realise there's no going back – you'll never see that pop-up again. But don't worry, I was hasty and didn't read it either and its never done me any harm.

After that go to 'my Apps' and add an app – I can't remember the full workflow here so you'll just have to stumble through it... Once you have an app go to settings->basic. Give the app a name and add your website domain. Select a category (I use 'entertainment' the nearest option they have to describe my website of social engineering and shared free-thinkers' content). Add the link to your privacy policy. In my website framework (if you're using it) you'll find one at '/policies/privacy-policy' – which you ought to edit for yourself.

Save your changes then at the bottom of the page elect to 'Add Platform'. Select website then enter your website domain (yeah I know, we did that earlier, but they make you do it again here). Then save changes (yeah I know, we did that earlier, but whatevs....)

Now at the top of the page switch your app from in development to live.

Now (at the top of the page) you'll have a usable App Id. You have an App Secret too – but we're no sniffing about inside peoples' data so we won't be needing that, thank you facebook.

This App Id needs to appear in a Meta tag on any page that integrates the facebook social plug-ins, as so:

<meta property="fb:app_id" content="[your 15-digit App Id]" />

If you want to be able to moderate the comments on your website you also need to add a meta tag to identify yourself as an administrator of the social plug-in integration:

<meta property="fb:admins" content="[your facebook user id]"/>

That's just about everything needed in the page header for the social plug-ins, but we also need to ensure that our pages share well when their URLs are placed directly into facebook posts. There's a whole bunch of additional meta tags for this, with oodles of redundancy – it's really quite ridiculous. But here they are... every page ought to include:

<meta property="og:image" content="[link to an image representing the page]" />
<meta property="og:image:width" content="[pixel width of the image]" />
<meta property="og:image:height" content="[pixel height of the image]" />
<meta name="fb_title" content="[I use the page's <title> tag value here" />
<meta property="og:type" content="article" />
<meta property="og:site_name" content="[your site name]" />
<meta property="article:author" content="[your name]" />
<meta property="og:description" content="[I use the first 5 lines of the article the page represents]" />
<meta property="og:title" content="[again! Yeah, use the same as before...]" />

NB. For image width and height I always set those to '1024', since I know all my images are at least that size. I don't bother working out the actual size, and it doesn't seem to cause me any trouble. I mean, they already have the image link, they could work that out for themselves; if they're not going to bother I don't see why I should. So I don't. And it works. So you shouldn't worry over much about it. Do supply the meta tags for width and height though.

Also, once you've created a page with full facebook integration before sharing it on facebook you should run the URL through the share debugger. facebook clearly realises that their 'easy integration' is the kind of thing that often needs painful debugging so instead of addressing that concern they've provided us with a lovely little laborious tool. Find it here: https://developers.facebook.com/tools/debug. Carefully check everything it says about your page and correct in the page as needs be (thereafter using the tool again and 're-scraping' the page). You might get transient errors (no image for example) – just wait a moment and re-scrape until it looks right. This also means you will have primed the facebook cache, so anyone else sharing the link should find all of the right info turns up. It's worth it...

Adding Social Plug-In Code

As I mentioned earlier, the documentation includes configuration tools that provide you with code you can put on your webpage to achieve the integration.

The like button configuration tool is found at: https://developers.facebook.com/docs/plugins/like-button and the comments configuration tool is found at: https://developers.facebook.com/docs/plugins/comments.

Both of these tools provide code (step 2) that should appear once on the page. If integrating both comments and likes use either of these (not both). They have different 'nonce' values (really facebook?) but I doubt that matters, just pick one and use it.

BUT both of these tools require a 'div' to be inserted (fb-root) which is a non-semantic intrusion upon the page's content structure – WTF facebook? That's pretty much as ugly as sin. So I don't bother with step 2 from these tools. Instead I jump over to the documentation pages from the facebook JavaScript SDK team (if only the social plug-in team knew there was a JavaScript SDK team, eh?) and I use their method to add the SDK to my pages. You can find that info at: https://developers.facebook.com/docs/javascript/quickstart/

and the code I use is:

<script>
  window.fbAsyncInit = function() {
    FB.init({
      appId            : '[your-app-id]',
      autoLogAppEvents : true,
      xfbml            : true,
      version          : 'v7.0'
    });
  };
</script>
<script async defer crossorigin="anonymous" src="https://connect.facebook.net/en_US/sdk.js"></script>

Because the SDK loads asynchronously I do add this immediately following the page's <body> tag.

You can then use the code in step 3 of the configuration tools to add like and comments plug-ins. The code I use is:

<div class="fb-like" data-href="[canonical URL]" data-width="60" data-layout="box_count" data-action="like" data-size="small" data-share="false" style="border:none;overflow:hidden; float:left;" ></div>
<div class="fb-comments" data-href="[clean URL]" data-numposts="5" data-mobile="true" data-width="100%" ></div>

And for the share function I embed an image with a link, as so:

<a href="https://www.facebook.com/sharer.php?u=[clean URL]" target="_blank">
<img class="small-button" src="http://antsmith.net/framework/images/share-buttons/facebook.png" alt="Facebook share button" />
</a>

Final Words

Bloated tangled mess of spaghetti – is what immediately leaps to mind. Anyway, I hope all this has been of help. In Summary you will need:

Response Headers (before any output)

header ('Content-Security-Policy: frame-ancestors https://www.facebook.com/');
header ('X-Frame-Options: ALLOW-FROM https://www.facebook.com/');

Link and Meta tags in the page <head>

<link rel = "canonical" href="http://antsmith.net/articles/cameras-of-my-life" >
<meta property="og:url" content="http://antsmith.net/articles/cameras-of-my-life/tag/photography" />
<meta property="fb:app_id" content="[your 15-digit App Id]" />
<meta property="fb:admins" content="[your facebook user id]"/>
<meta property="og:image" content="[link to an image representing the page]" />
<meta property="og:image:width" content="[pixel width of the image]" />
<meta property="og:image:height" content="[pixel height of the image]" />
<meta name="fb_title" content="[I use the page's <title> tag value here" />
<meta property="og:type" content="article" />
<meta property="og:site_name" content="[your site name]" />
<meta property="article:author" content="[your name]" />
<meta property="og:description" content="[I use the first 5 lines of the article the page represents]" />
<meta property="og:title" content="[again! Yeah, use the same as before...]" />

Scripts, Divs and Links in the body

<script>
  window.fbAsyncInit = function() {
    FB.init({
      appId            : '[your-app-id]',
      autoLogAppEvents : true,
      xfbml            : true,
      version          : 'v7.0'
    });
  };
</script>
<script async defer crossorigin="anonymous" src="https://connect.facebook.net/en_US/sdk.js"></script>
<div class="fb-like" data-href="http://antsmith.net/articles/cameras-of-my-life"...>
<a target="_blank" rel="nofollow" href="https://www.facebook.com/sharer.php?u=/articles/cameras-of-my-life/tag/photography"...>
<div class="fb-comments" data-href="http://antsmith.net/articles/cameras-of-my-life/tag/photography"...>

Articles