Facebook's opengraph and concrete 5

With a few changes to your header file you can make sharing your web pages a whole lot better on your concrete 5 website.  Facebook uses a protocol called opengraph to determine what information is displayed to the person's stream, and you can control how that is set from the metadata of your page. Keep reading to find out exactly how.

I'm writing this working with concrete 5.4.2.2, so you may need to adjust slightly to match the actual code in your header file if you are using a different version of concrete 5.  I thought about making this into a package then realized that I would need to make a version to work with every version of concrete 5 and that didn't sound like a lot of fun to test.  So I think a tutorial works - if you are a developer looking to put the fine detail finish on your websites you will probably create your own copy of the header_required.php and copy it from project to project.  That's what I do.  If you are a site owner you'd need to do this or hire someone to do this each time there is an update to concrete 5. 

The first thing you're going to need to do to get started is to override your 'header_required' file.  Copy it from /concrete/elements/header_required.php to /elements/header_required.php.  Look for the 'if (is_object($c){' call at the top of the file.  Change it from this:

  1. if (is_object($c)) {
  2. $pageTitle = (!$pageTitle) ? $c->getCollectionName() : $pageTitle;
  3. $pageDescription = (!$pageDescription) ? $c->getCollectionDescription() : $pageDescription;
  4. $cID = $c->getCollectionID();
  5. $isEditMode = ($c->isEditMode()) ? "true" : "false";
  6. $isArrangeMode = ($c->isArrangeMode()) ? "true" : "false";
  7.  
  8. } else {
  9. $cID = 1;
  10. }

to this:

  1. if (is_object($c)) {
  2. $pageTitle = (!$pageTitle) ? $c->getCollectionName() : $pageTitle;
  3. $pageDescription = (!$pageDescription) ? $c->getCollectionDescription() : $pageDescription;
  4. $cID = $c->getCollectionID();
  5. $isEditMode = ($c->isEditMode()) ? "true" : "false";
  6. $isArrangeMode = ($c->isArrangeMode()) ? "true" : "false";
  7. $nav = Loader::helper('navigation');
  8. $og_url = $nav->getLinkToCollection($c, true);
  9. } else {
  10. $cID = 1;
  11. }

We're defining the sharing url here, to output later.  This will be used by facebook when it indexes and caches your content, so you want to make sure it's defined. This way even if you have a page alias or if you have ?cID=## style links you can still define a pretty link to your page.

After this we're going to want to define the sharing image.  Add a collection attribute of type image/file called "Sharing Image" with the handle "sharing_image."  Scroll down a little further in the file and look for where it defines the attributes for meta title, description and keyword, add this after to define the value of the sharing image:

  1. $akk = $c->getCollectionAttributeValue('meta_keywords');
  2. $akpp = $c->getCollectionAttributeValue('sharing_image');
  3. $home = Page::getByID(HOME_CID);
  4. $akhpp = $home->getCollectionAttributeValue('sharing_image');
  5.  
  6. if ($akpp) {
  7. $sharing_image = $akpp;
  8. } else {
  9. $sharing_image = $akhpp;
  10. }

First we check the value for the current page, then we check the value for the home page.  If there's nothing defined for the current page, then we inherit from the home page.  This means that the home page sharing image must be defined.  This will be the main 'fallback' image for your site, so it should be something like your corporate logo or something.  Keep in mind that it will be in a small square box when shared, so keep your proportions even.

Next we want to define the title for sharing.  Look for the part saying if $(akt) and change it from this:

  1. if ($akt) {
  2. $pageTitle = $akt;
  3. ?><title><?php echo htmlspecialchars($akt, ENT_COMPAT, APP_CHARSET)?></title>
  4. <?php } else {
  5. $pageTitle = htmlspecialchars($pageTitle, ENT_COMPAT, APP_CHARSET);
  6. ?><title><?php echo sprintf(PAGE_TITLE_FORMAT, SITE, $pageTitle)?></title>
  7. <?php }

to this:

  1. if ($akt) {
  2. $pageTitle = $akt;
  3. ?><title><?php echo htmlspecialchars($akt, ENT_COMPAT, APP_CHARSET)?></title>
  4. <meta property="og:title" content="<?php echo htmlspecialchars($akt, ENT_COMPAT, APP_CHARSET) ?>" />
  5. <?php } else {
  6. $pageTitle = htmlspecialchars($pageTitle, ENT_COMPAT, APP_CHARSET);
  7. ?><title><?php echo sprintf(PAGE_TITLE_FORMAT, SITE, $pageTitle)?></title>
  8. <meta property="og:title" content="<?php echo sprintf(PAGE_TITLE_FORMAT, SITE, $pageTitle) ?>" />
  9. <? }

After this we do the same thing with description.  Change this:

  1. if ($akd) { ?>
  2. <meta name="description" content="<?php echo htmlspecialchars($akd, ENT_COMPAT, APP_CHARSET)?>" />
  3. <?php } else { ?>
  4. <meta name="description" content="<?php echo htmlspecialchars($pageDescription, ENT_COMPAT, APP_CHARSET)?>" />
  5. <?php }

to this:

  1. if ($akd) { ?>
  2. <meta name="description" content="<?=htmlspecialchars($akd, ENT_COMPAT, APP_CHARSET)?>" />
  3. <meta property="og:description" content="<?php echo htmlspecialchars($akd, ENT_COMPAT, APP_CHARSET) ?>" />
  4. <?php } else { ?>
  5. <meta name="description" content="<?=htmlspecialchars($pageDescription, ENT_COMPAT, APP_CHARSET)?>" />
  6. <meta property="og:description" content="<?php echo htmlspecialchars($pageDescription, ENT_COMPAT, APP_CHARSET) ?>" />
  7. <?php }

Open graph doesn't have the notion of keywords, so leave that portion of the document untouched. Next up the file checks to see if the page is added to the search index, we don't want to change this either.  Look for the closing php tag after that if statement, and then add in the following after.  It should look something like this:

  1. if($c->getCollectionAttributeValue('exclude_search_index')) { ?>
  2. <meta name="robots" contents="noindex" />
  3. <?php } ?>
  4.  
  5. <meta property="og:url" content="<?php echo $og_url ?>"/>
  6. <?php if (isset ($sharing_image)){?>
  7. <link rel="image_src" href="<?php echo($sharing_image->getVersion()->getURL()) ?>" />
  8. <meta property="og:image" content="<?php echo($sharing_image->getVersion()->getURL()) ?>" />
  9. <?php } ?>
  10. <meta property="og:site_name" content="<?php echo SITE;?>" />

Here we've actually output the url for the sharing image and set the link rel="image_src" for other search engines that support it.  We've also output the url we defined at the top of the file.  To show which site this is from, we've also defined the site name as well. 

And that's really all the more there is to it.  You should be able to share your pages to facebook now and see the information from your metadata fields properly defined to the title and description.  The thumbnail you define for the page will be used as the thumbnail image, and the site name and url will be defined exactly as you specify. 

When you put it all together, you should have a header_required.php that looks like this:

  1. <?php
  2. defined('C5_EXECUTE') or die("Access Denied.");
  3. global $c;
  4. global $cp;
  5. global $cvID;
  6.  
  7. if (is_object($c)) {
  8. $pageTitle = (!$pageTitle) ? $c->getCollectionName() : $pageTitle;
  9. $pageDescription = (!$pageDescription) ? $c->getCollectionDescription() : $pageDescription;
  10. $cID = $c->getCollectionID();
  11. $isEditMode = ($c->isEditMode()) ? "true" : "false";
  12. $isArrangeMode = ($c->isArrangeMode()) ? "true" : "false";
  13. $nav = Loader::helper('navigation');
  14. $og_url = $nav->getLinkToCollection($c, true);
  15. } else {
  16. $cID = 1;
  17. }
  18. ?>
  19.  
  20. <meta http-equiv="content-type" content="text/html; charset=<?php echo APP_CHARSET?>" />
  21. <?php
  22. $akt = $c->getCollectionAttributeValue('meta_title');
  23. $akd = $c->getCollectionAttributeValue('meta_description');
  24. $akk = $c->getCollectionAttributeValue('meta_keywords');
  25. $akpp = $c->getCollectionAttributeValue('sharing_image');
  26. $home = Page::getByID(HOME_CID);
  27. $akhpp = $home->getCollectionAttributeValue('sharing_image');
  28.  
  29. if ($akpp) {
  30. $sharing_image = $akpp;
  31. } else {
  32. $sharing_image = $akhpp;
  33. }
  34.  
  35. if ($akt) {
  36. $pageTitle = $akt;
  37. ?><title><?php echo htmlspecialchars($akt, ENT_COMPAT, APP_CHARSET)?></title>
  38. <meta property="og:title" content="<?php echo htmlspecialchars($akt, ENT_COMPAT, APP_CHARSET) ?>" />
  39. <?php } else {
  40. $pageTitle = htmlspecialchars($pageTitle, ENT_COMPAT, APP_CHARSET);
  41. ?><title><?php echo sprintf(PAGE_TITLE_FORMAT, SITE, $pageTitle)?></title>
  42. <meta property="og:title" content="<?php echo sprintf(PAGE_TITLE_FORMAT, SITE, $pageTitle) ?>" />
  43. <? }
  44.  
  45. if ($akd) { ?>
  46. <meta name="description" content="<?=htmlspecialchars($akd, ENT_COMPAT, APP_CHARSET)?>" />
  47. <meta property="og:description" content="<?php echo htmlspecialchars($akd, ENT_COMPAT, APP_CHARSET) ?>" />
  48. <?php } else { ?>
  49. <meta name="description" content="<?=htmlspecialchars($pageDescription, ENT_COMPAT, APP_CHARSET)?>" />
  50. <meta property="og:description" content="<?php echo htmlspecialchars($pageDescription, ENT_COMPAT, APP_CHARSET) ?>" />
  51. <?php }
  52. if ($akk) { ?>
  53. <meta name="keywords" content="<?=htmlspecialchars($akk, ENT_COMPAT, APP_CHARSET)?>" />
  54. <?php }
  55. if($c->getCollectionAttributeValue('exclude_search_index')) { ?>
  56. <meta name="robots" contents="noindex" />
  57. <?php } ?>
  58.  
  59. <meta property="og:url" content="<?php echo $og_url ?>"/>
  60. <?php if (isset ($sharing_image)){?>
  61. <link rel="image_src" href="<?php echo($sharing_image->getVersion()->getURL()) ?>" />
  62. <meta property="og:image" content="<?php echo($sharing_image->getVersion()->getURL()) ?>" />
  63. <?php } ?>
  64. <meta property="og:site_name" content="<?php echo SITE;?>" />
  65. <meta name="generator" content="concrete5 - <?php echo APP_VERSION ?>" />
  66.  
  67. <?php $u = new User(); ?>
  68. <script type="text/javascript">
  69. <?php
  70. echo("var CCM_DISPATCHER_FILENAME = '" . DIR_REL . '/' . DISPATCHER_FILENAME . "';\r");
  71. echo("var CCM_CID = ".($cID?$cID:0).";\r");
  72. if (isset($isEditMode)) {
  73. echo("var CCM_EDIT_MODE = {$isEditMode};\r");
  74. }
  75. if (isset($isEditMode)) {
  76. echo("var CCM_ARRANGE_MODE = {$isArrangeMode};\r");
  77. }
  78. ?>
  79. var CCM_IMAGE_PATH = "<?php echo ASSETS_URL_IMAGES?>";
  80. var CCM_TOOLS_PATH = "<?php echo REL_DIR_FILES_TOOLS_REQUIRED?>";
  81. var CCM_REL = "<?php echo DIR_REL?>";
  82.  
  83. </script>
  84.  
  85. <?php
  86. $html = Loader::helper('html');
  87. $this->addHeaderItem($html->css('ccm.base.css'), 'CORE');
  88. $this->addHeaderItem($html->javascript('jquery.js'), 'CORE');
  89. $this->addHeaderItem($html->javascript('ccm.base.js'), 'CORE');
  90. defined the url
  91. $favIconFID=intval(Config::get('FAVICON_FID'));
  92.  
  93.  
  94. if($favIconFID) {
  95. $f = File::getByID($favIconFID); ?>
  96. <link rel="shortcut icon" href="<?php echo $f->getRelativePath()?>" type="image/x-icon" />
  97. <link rel="icon" href="<?php echo $f->getRelativePath()?>" type="image/x-icon" />
  98. <?php } ?>
  99.  
  100. <?php
  101. if (is_object($cp)) {
  102.  
  103. if ($this->editingEnabled()) {
  104. Loader::element('page_controls_header', array('cp' => $cp, 'c' => $c));
  105. }
  106.  
  107. if ($this->areLinksDisabled()) {
  108. $this->addHeaderItem('<script type="text/javascript">window.onload = function() {ccm_disableLinks()}</script>', 'CORE');
  109. }
  110.  
  111. }
  112.  
  113. print $this->controller->outputHeaderItems();
  114. echo $c->getCollectionAttributeValue('header_extra_content');

There's one attribute in the opengraph protocol that we're not using here, and that's 'type.'  You could set up a select attribute type for this and add in the list of supported types as options, then output the value for that to another og:type meta tag if you wanted, I haven't really had the need to get that detailed in any projects that I've done so I haven't gotten into it.  

blog comments powered by Disqus