How to create a Jetpack Site Stats Aggregator

👋 Ciprian on Thursday, October 7, 2021 in Blog
Last modified on Thursday, October 7, 2021

Learn JavaScript by example. Code snippets, how-to's and tutorials. Try now!

Jetpack Site Stats Aggregator

I believe that Jetpack Site Stats doesn’t need an introduction anymore. It provides basic but good insights on your website users. It’s actually a pretty decent alternative to the new Google Analytics GA4 for small to medium websites.

Today we are going to create a Jetpack Stats Aggregator and see all our connected website stats on one page. We are going to use the ChartJS library to generate pretty charts.

Let’s start with the initial authentication. The code below will setup a page so we can connect with our WordPress.com account. You will need your WordPress.com client ID and client secret.

The first thing you need to do is create a new WordPress.com application. This will give you a chance to describe your application and how WordPress.com should communicate with it. You should give your app the same title as your website as that information is used in the login form users see. Once configured, you will receive your CLIENT ID and CLIENT SECRET to identify your app.

Replace items in bold with your values.

index.php

<html>
<head>
<title>Jetpack Stats Aggregator</title>
</head>
<body>
<?php
session_start();

define('CLIENT_ID', 12345);
define('CLIENT_SECRET', 'clientsecretstringhere');
define('REDIRECT_URL', 'https://www.example.com/stats.php');
define('REQUEST_TOKEN_URL', 'https://public-api.wordpress.com/oauth2/token');
define('AUTHENTICATE_URL', 'https://public-api.wordpress.com/oauth2/authenticate');

$wpcc_state = md5(mt_rand());
$_SESSION['wpcc_state'] = $wpcc_state;

$url_to = AUTHENTICATE_URL . '?' . http_build_query([
    'response_type' => 'code',
    'scope'         => 'global',
    'client_id'     => CLIENT_ID,
    'state'         => $wpcc_state,
    'redirect_uri'  => REDIRECT_URL,
]);

echo '<a href="' . $url_to . '"><img src="https://s0.wp.com/i/wpcc-button.png" width="231" alt="Authenticate"></a>';
?>
</body>
</html>

This page will look like this:

After we successfully connect with WordPress.com, we will be redirected to the next page – stats.php.

Next, we create the actual Jetpack Stats aggregator page. See the comments in the code and, again, replace items in bold with your values.

stats.php

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Jetpack Stats Aggregator</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
    box-sizing: border-box;
}
body {
    background-color: #222f3e;
    color: #c9d1d9;

    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI Variable", "Segoe UI", system-ui, ui-sans-serif, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
    font-size: 14px;
    line-height: 1.5;
}

.cell {
    flex-basis: 48%;
    padding: 16px;
    box-shadow: 0 0 16px rgba(0, 0, 0, 0.25);
    border: 1px solid rgba(0, 0, 0, 0.5);
    margin-bottom: 24px;
}
</style>

<script src="https://cdn.jsdelivr.net/npm/chart.js@3.5.1/dist/chart.min.js"></script>
</head>
<body>

<?php
define('CLIENT_ID', 12345);
define('CLIENT_SECRET', 'clientsecretstringhere');
define('REDIRECT_URL', 'https://www.example.com/stats.php');
define('REQUEST_TOKEN_URL', 'https://public-api.wordpress.com/oauth2/token');
define('AUTHENTICATE_URL', 'https://public-api.wordpress.com/oauth2/authenticate');

/**
 * If no code has been passed as a parameter, exit, as it's not a valid request.
 */
if (!isset($_GET['code'])) {
    die('Warning! Visitor may have declined access or navigated to the page without being redirected.');
}
 
session_start();



/**
 * If the passed state parameter is not the same as the one in the previous session, exit, as it's not a valid request.
 */
if ($_GET['state'] !== $_SESSION['wpcc_state']) {
    die('Warning! State mismatch. Authentication attempt may have been compromised.');
}

/**
 * Authenticate with your WordPress.com credentials to get an access token.
 * Note that you need to disable 2FA in order to use this feature.
 */
$curl = curl_init(REQUEST_TOKEN_URL);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, [
    'client_id' => CLIENT_ID,
    'client_secret' => CLIENT_SECRET,
    'grant_type' => 'password',
    'username' => 'name@email.com',
    'password' => 'yourwordpresscompasswordhere',
]);
 
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$auth = curl_exec($curl);
$secret = json_decode($auth);

echo '<pre>';
print_r($auth);
echo '</pre>';

$access_token = $secret->access_token;

/**
 * Use the access token to get a list of all the sites with Jetpack installed and Site Stats enabled
 */ 
$curl = curl_init("https://public-api.wordpress.com/rest/v1.1/jetpack-blogs/");
curl_setopt($curl, CURLOPT_HTTPHEADER, array( 'Authorization: Bearer ' . $access_token));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($curl);
$blog_ids = json_decode($response, true);
$blog_ids = $blog_ids['blogs']['blogs'];

/**
 * Loop through all sites and generate a dropdown list
 */
echo '<select id="site-selector">';
    foreach ($blog_ids as $blog) {
        echo '<option value="' . $blog['userblog_id'] . '">' . $blog['blogname'] . ' (' . $blog['siteurl'] . ')</option>';
    }
echo '</select>';



/**
 * Loop through all sites and display pretty boxes and a nice ChartJS graph
 */
echo '<div style="display: flex; flex-wrap: wrap; justify-content: space-around;">';

foreach ($blog_ids as $blog) {
    $options = [
        'http' => [
            'ignore_errors' => true,
            'header' => [
                0 => "authorization: Bearer $access_token",
            ],
        ],
    ];

    $context  = stream_context_create($options);
    $response = file_get_contents('https://public-api.wordpress.com/rest/v1.1/sites/' . $blog['userblog_id'] . '/stats', false, $context);
    $response = (array) json_decode($response);
    $visits = (array) $response['visits'];
    $data = (array) $visits['data'];

    echo '<div class="cell">';
    echo '<h2 id="blog-' . $blog['userblog_id'] . '">' . $blog['blogname'] . '</h2>';
    echo '<p><code>' . $blog['siteurl'] . '</code></p>';

    $dayArray = [];
    $dateArray = [];
    $usersArray = [];
    foreach ($data as $day) {
        $dateArray[] = $day[0];
        $dayArray[] = $day[1];
        $usersArray[] = $day[2];
    }

    $labels = "'" . implode("','", $dateArray) . "'";

    echo '<canvas id="chart-' . $blog['userblog_id'] . '" width="500" height="200"></canvas>
    <script>
    var ctx = document.getElementById("chart-' . $blog['userblog_id'] . '").getContext("2d");
    var myChart = new Chart(ctx, {
        type: "bar",
        data: {
            labels: [' . $labels . '],
            datasets: [{
                label: "Users",
                data: [' . implode(',', $usersArray) . '],
                backgroundColor: "#f5cd79",
                borderWidth: 1
            }, {
                label: "Pageviews",
                data: [' . implode(',', $dayArray) . '],
                backgroundColor: "#546de5",
                borderWidth: 1
            }]
        },
        options: {
            responsive: true,
            scales: {
                x: {
                    stacked: true,
                },
                y: {
                    stacked: false,
                    beginAtZero: true,
                },
            },
            plugins: {
            legend: {
                display: true,
                labels: {
                    color: "#c9d1d9"
                }
            }
        }
        }
    });
    </script>';

    $dayArray = [];
    $dateArray = [];
    $usersArray = [];

    echo '</div>';
}
echo '</div>';
?>

<script>
document.getElementById("site-selector").addEventListener("change", event => {
    console.log(document.getElementById("site-selector").value);
    location.href = '#blog-' + document.getElementById("site-selector").value;
});
</script>

</body>
</html>

I have also created a small dropdown at the top of the page to quickly jump to any website down the page.

Here is the final page result:

Jetpack Site Stats Aggregator

Demo

See a live demo below. Login using your WordPress.com credentials and see all your websites’ stats in one place.

Connect with WordPress.com

Note: I have packaged the above demo as a WordPress plugin so I can use it on any site I want.

Buy me a coffee to support my work!

👋 Added by Ciprian on Thursday, October 7, 2021 in Blog. Last modified on Thursday, October 7, 2021.

Leave a Reply

You have to agree to the comment policy.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Privacy Policy