At the bottom of MODX.today articles is a simple counter that shows how many times a post has been shared. This includes data from Facebook, Twitter, LinkedIn and Google+. The code is pretty simple, so if you want to do the same, we've got you covered!

Step 1: The Snippet

We've done the hard work for you, and wrote a snippet that asks Facebook, Twitter, LinkedIn and Google Plus for their total share counts. Copy/paste the code below into a new snippet and give it the name getShareCount.

This snippet does not necessarily use official APIs, as not all of the services expose official APIs to get the number of times a link was shared, so it might break if they decide to no longer support it. 

The snippet also includes caching (15 minutes) to make sure it isn't constantly waiting for those third party APIs, and it will return the share data as a simple JSON-encoded array. Aside from the grand total number of shares, it also contains the count split up for each of the data sources. 

<?php
$resourceId = $modx->getOption('resource', $_REQUEST, $modx->resource->get('id'));
$url = $modx->makeUrl($resourceId, '', '', 'full');
$urlHash = md5($url);

// Check if we have cached data first
$cached = $modx->cacheManager->get('shares/' . $urlHash);
if (is_array($cached)) {
    return $modx->toJSON($cached);
}

// No cache? Then this is where we'll grab the data.
$urlEnc = urlencode($url);
$total = 0;
$results = array(
    'url' => $url,
    'sources' => array(),
);

// Facebook
$fbUrl = "http://api.facebook.com/restserver.php?method=links.getStats&format=json&urls={$urlEnc}";
$fbCount = reset($modx->fromJSON(file_get_contents($fbUrl)));
if (is_array($fbCount) && isset($fbCount['total_count'])) {
    $total += $fbCount['total_count'];
    $results['sources']['facebook'] = $fbCount['total_count'];
}

// Twitter - this uses an undocumented endpoint which could break in the future
$twitterUrl = "https://cdn.api.twitter.com/1/urls/count.json?url={$urlEnc}";
$twitterCount = $modx->fromJSON(file_get_contents($twitterUrl));

if (is_array($twitterCount) && isset($twitterCount['count'])) {
    $total += $twitterCount['count'];
    $results['sources']['twitter'] = $twitterCount['count'];
}

// Google+ - this uses an undocumented endpoint which could break in the future
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, "https://clients6.google.com/rpc");
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, '[{"method":"pos.plusones.get","id":"p","params":{"nolog":true,"id":"' . $url . '","source":"widget","userId":"@viewer","groupId":"@self"},"jsonrpc":"2.0","key":"p","apiVersion":"v1"}]');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
$curl_results = curl_exec ($curl);
curl_close ($curl);
$json = json_decode($curl_results, true);

// The number we're looking for is hidden deep!
if (isset($json[0]['result']['metadata']['globalCounts']['count'])) {
    $total += $json[0]['result']['metadata']['globalCounts']['count'];
    $results['sources']['googleplus'] = $json[0]['result']['metadata']['globalCounts']['count'];
}

// LinkedIn
$linkedinUrl = "https://www.linkedin.com/countserv/count/share?url={$urlEnc}&format=json";
$linkedinCount = $modx->fromJSON(file_get_contents($linkedinUrl));
if (is_array($linkedinCount) && isset($linkedinCount['count'])) {
    $total += $linkedinCount['count'];
    $results['sources']['linkedin'] = $linkedinCount['count'];
}

// Grand total
$results['total'] = $total;

// Write it to cache
$modx->cacheManager->set('shares/' . $urlHash, $results, 60 * 15);
return $modx->toJSON($results);

Step 2: The Resource

To make sure the rendering of the page is not delayed if one of the services is slow to respond, we wont be including the snippet into the article pages directly. Instead, we'll use a touch of AJAX to load it asynchronously. This works great in our use case, where the share counts are "below the fold" but you might need to do some extra work if you want to show share counts at the top of the page.

To load the data via AJAX, we'll need a resource that contains the snippet, which returns the JSON-encoded array from the snippet. 

Create a new resource with the following values:

  • Pagetitle: whatever you want, probably something along the lines of "Shares"
  • Alias: whatever you want, but for this article we'll assume you used "shares"
  • Template: empty
  • Published and Hidden from Menu
  • Content Type: JSON
  • Rich Text and Searchable off 
  • If you useContentBlocks, make sure to disable that on the resource too as that can inject markup from your layouts.

In the content of the resource, just call the getShareCount snippet. Make sure it is uncached. 

[[!getShareCount]]

Step 3: The JavaScript

Somewhere in your Article template, use the following piece of JavaScript to load the share counts after the page is ready. Place it at the bottom of the page, right before the closing </body> tag. 

Please note that this code makes use of jQuery - make sure it is loaded, or rewrite it if you're not using jQuery at all. In the example, we're shooting over the AJAX request as soon as the page is done loading, but you could also make it trigger when scrolling past a certain point of the page, or after a timeout depending on your use case.

$(document).on('ready', function() {
    $.ajax({
        dataType: "json",
        url: '[[~SNIPPET_RESOURCE_ID_HERE? &resource=`[[*id]]`]]',
        success: function(data) {
            if (data.total) {
                $('.total_shares').text(data.total);
            }
            if (data.sources.facebook > 0) {
                $('.total_facebook_shares').text(data.sources.facebook);
            }
            if (data.sources.twitter > 0) {
                $('.total_twitter_shares').text(data.sources.twitter);
            }
            if (data.sources.googleplus > 0) {
                $('.total_googleplus_shares').text(data.sources.googleplus);
            }
            if (data.sources.linkedin > 0) {
                $('.total_linkedin_shares').text(data.sources.linkedin);
            }
        }
    });
});

Make sure to replace SNIPPET_RESOURCE_ID_HERE with the ID of the resource you created earlier that contains the snippet call (but don't replace the &resource part!). If you add the code to an external javascript file, you'll need to find a different way to get the url into the function, perhaps by setting a global variable through MODX, or simply hardcoding the url and fetching the resource ID from a data attribute. 

You'll also need to make sure that you're inserting the numbers into the right place. We're only showing the total on MODX.today, but in the example code above you can see it is targeting special classes for the per-source share counts.

To see it in action, see the big number below and be sure to share this article so the number goes up!