UPDATE - I fixed a bug in the code. My copy and paste skills let
me down :)
We use the MultiSitesPluging
for Piwik to get a view of all
the traffic across a collection of sites. It allows us to see all
the basic stats for multiple websites on one page. A few weeks ago,
I made a few updates to the plugin to aggregate the data for the
all the sites, and since someone else asked about it, thought I
would document what I did :)
First was a few updates to MultiSitesPlugin/Controller.php
$total_visits = 0;
$total_actions = 0;
$total_unique = 0;
if(count($my_sites) == 1 )
{
$my_sites[0]['visits'] = $visits_piwik;
$my_sites[0]['actions'] = $actions_piwik;
$my_sites[0]['unique'] = $unique_users;
$my_sites[0]['sparkline'] =
$sparklines_piwik_array[$my_sites[0]['idsite']];
$my_sites[0]['summary'] =
$summary['text'];
$my_sites[0]['summary_value'] =
$summary['value'];
$total_visits = $visits_piwik;
$total_actions = $actions_piwik;
$total_unique = $unique_users;
}
else
{
$visits_piwik_array =
$visits_piwik->getArray();
$actions_piwik_array =
$actions_piwik->getArray();
$unique_users_piwik_array =
$unique_users->getArray();
foreach($my_sites as &$site)
{
$site['visits'] =
$visits_piwik_array[$site['idsite']]->getColumn(0);
$site['actions'] =
$actions_piwik_array[$site['idsite']]->getColumn(0);
$site['unique'] =
$unique_users_piwik_array[$site['idsite']]->getColumn(0);
$site['sparkline'] =
$sparklines_piwik_array[$site['idsite']];
$site['summary'] =
$summary[$site['idsite']]['text'];
$site['summary_value']
= $summary[$site['idsite']]['value'];
$total_visits =
$total_visits +
$visits_piwik_array[$site['idsite']]->getColumn(0);
$total_actions =
$total_actions +
$actions_piwik_array[$site['idsite']]->getColumn(0);
$total_unique =
$total_unique +
$unique_users_piwik_array[$site['idsite']]->getColumn(0);
}
usort($my_sites,
array("Piwik_MultiSitesPlugin_Controller",
"sort_by_".$this->order));
}
$this->count = count($my_sites);
as well as the addition of a few lines here:
$view->page = $this->page;
$view->limit = $this->limit;
$view->count = $this->count;
$view->order_by = $this->order_by;
$view->order = $this->order;
$view->page = $this->page;
$view->total_visits = $total_visits;
$view->total_actions = $total_actions;
$view->total_unique = $total_unique;
$view->total_site_count = count($my_sites);
echo $view->render();
I then created a new template file in the templates directory
called total.tpl:
<tr align="center" style="border-right: 1px solid
black;">
<td align="center" class="column">
</td>
<td align="left" class="column" id="siteName">
<b>TOTAL ({$total_site_count})</b>
</td>
<td align="right" class="column" id="visits">
{$total_visits} </td>
</td>
<td align="right" class="column" id="actions">
{$total_actions} </td>
</td>
<td align="right" class="column" id="unique">
{$total_unique} </td>
</td>
<td align="center" class="column" id="sparkline">
</td>
<td class="column"></td>
</tr>
Lastly, I modified the templates/index.tpl file and added the
following line (in bold)
foreach from=$my_sites key=i item=site}
{include file='MultiSitesPlugin/templates/row.tpl'}
{/foreach}
{include
file='MultiSitesPlugin/templates/total.tpl'}
<tr>
I hope that helps those that are interested :)
I'm using the excellent Piwik analytics tool on a number
of sites hosted on a central CMS platform. The problem I found was
that the JavaScript
snippet required to track each user has a unique ID in it that
identifies the the site. This was going to be a pain, so I modified
the code to allow the each visit to use the site ID if it was
present, or alternatively, work out the site ID from the
URL.
The code below is replacing the function __construct() in the
file piwik/core/Tracker/Visit.php:
function __construct()
{
$idsite =
Piwik_Common::getRequestVar('idsite', 0, 'int',
$this->request);
// No idsite passed
in, so try and get from URL
if($idsite <=
0)
{
$host = '';
// get host name from URL
$url = Piwik_Common::getRequestVar('url');
preg_match('@^(?:http://)?([^/]+)@i', $url,
$urlmatches);
$host = "http://" .
str_ireplace('www.', '', $urlmatches[1]);
if ($host != '')
{
// If no site ID, lets look up URL...
$idsiteRow = Piwik_Tracker::getDatabase()->fetch("
SELECT idsite
FROM piwik_site
WHERE main_url = ?
LIMIT 1",
array($host)
);
if ($idsiteRow && count($idsiteRow) > 0)
{
$idsite = $idsiteRow['idsite'];
}
}
}
if($idsite <=
0)
{
throw new Exception("The 'idsite' is invalid");
}
$this->idsite =
$idsite;
}
Doing some basic performance testing, the code above only adds
on extra DB call which in my circumstances is acceptable. But, if
you really wanted, you could add some caching as well to improve
speed.
I have been working with some PHP
sites lately, and considering I'm a .NET kind of guy, have actually
(surprisingly?) enjoyed the experience :)
One of the cool new platforms I found was Piwik, the open source analytics
platform that is designed to be a competitor to Google Analytics (GA).
Now, I am a fan of GA, but in this case, we needed to own the stats
data, and do a lot of custom analysis and modelling on the data, so
I had to choose something where we could host it, and that's where
Piwik came in.
It was really easy to install and I got it
up and running in no time. The plug-in framework is awesome;
extremely powerful and flexible. Its easy to
build and deploy your own plug-ins, and the API to access the
data is clean, and so far, very fast.
To deploy on each site, it is as simple as GA - just add a JavaScript
tracking code. But I have a case where the client couldn't use
assets from 3rd party sites. The JavaScript tracking code
effectively generates an IMG (image) tag in HTML, and their legal
people wouldn't allow it.
So I created the proxy class below to allow the browser request
to be sent to a file hosted on the clients site, and in turn, this
proxy will hand off the request and all important information back
to the stats server we are running. Not sure if anyone else will
need it, but thought I would share anyway :)
<?php
// DEBUGGING - comment out when live
// error_reporting(E_ALL);
// ini_set('display_errors','1');
// Our vars
$url = 'http://statsserver.com/piwik/piwik.php?';
$referer = "";
$ua = "";
$lang = "";
$cookie = "";
$ip = "0.0.0.0";
// Build up URI from query string - need encode the value
foreach($_GET as $key => $value)
{
$url .= $key . "=" . urlencode($value) .
"&";
}
// Now set some headers
if (isset($_SERVER['HTTP_REFERER']))
$referer = $_SERVER['HTTP_REFERER'];
if (isset($_SERVER['HTTP_USER_AGENT']))
$ua = $_SERVER['HTTP_USER_AGENT'];
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
$lang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
if (isset($_SERVER['REMOTE_ADDR']))
$ip = $_SERVER['REMOTE_ADDR'];
$opts = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-Language: " . $lang . "\r\n" .
"User-Agent: " . $ua . "\r\n" .
"Referer: " . $referer . "\r\n" .
"X_Forwarded_For: " . $ip . "\r\n"
)
);
$headers = stream_context_create($opts);
// Send the request
$contents = file_get_contents($url, FILE_BINARY, $headers);
// Find the cookie from the response and pass back to the
client
$nlines = count($http_response_header);
for ($i = $nlines-1; $i >= 0; $i--)
{
$line = $http_response_header[$i];
if (substr_compare($line, 'Set-Cookie', 0, 10,
true) == 0)
{
$cookie = $line;
break;
}
}
header('Set-Cookie: ' . $cookie);
header('Content-Type: image/gif');
header('Pragma: no-cache');
header('Cache-Control: private, no-cache, proxy-revalidate');
echo $contents;
?>