2018-06-10 13:54:55 +02:00
|
|
|
<?php
|
|
|
|
/*
|
2018-09-03 14:45:13 +02:00
|
|
|
This plugin uses the javascript library https://github.com/gionkunz/chartist-js
|
2018-06-10 13:54:55 +02:00
|
|
|
*/
|
|
|
|
class pluginSimpleStats extends Plugin {
|
|
|
|
|
|
|
|
private $loadOnController = array(
|
|
|
|
'dashboard'
|
|
|
|
);
|
|
|
|
|
|
|
|
public function init()
|
|
|
|
{
|
2018-08-08 00:16:35 +02:00
|
|
|
global $L;
|
|
|
|
|
2018-06-10 13:54:55 +02:00
|
|
|
// Fields and default values for the database of this plugin
|
|
|
|
$this->dbFields = array(
|
2018-08-08 00:16:35 +02:00
|
|
|
'label'=>$L->g('Visits'),
|
2018-06-10 13:54:55 +02:00
|
|
|
'numberOfDays'=>7,
|
2018-09-03 14:45:13 +02:00
|
|
|
'excludeAdmins'=>false,
|
|
|
|
'showContentStats'=>false
|
2018-06-10 13:54:55 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function form()
|
|
|
|
{
|
2018-08-05 17:54:20 +02:00
|
|
|
global $L;
|
2018-06-10 13:54:55 +02:00
|
|
|
|
2018-07-01 14:17:24 +02:00
|
|
|
$html = '<div class="alert alert-primary" role="alert">';
|
|
|
|
$html .= $this->description();
|
|
|
|
$html .= '</div>';
|
|
|
|
|
|
|
|
$html .= '<div>';
|
2018-08-05 17:54:20 +02:00
|
|
|
$html .= '<label>'.$L->get('Label').'</label>';
|
2018-06-10 13:54:55 +02:00
|
|
|
$html .= '<input id="jslabel" name="label" type="text" value="'.$this->getValue('label').'">';
|
2018-08-05 17:54:20 +02:00
|
|
|
$html .= '<span class="tip">'.$L->get('This title is almost always used in the sidebar of the site').'</span>';
|
2018-06-10 13:54:55 +02:00
|
|
|
$html .= '</div>';
|
|
|
|
|
2018-09-03 14:45:13 +02:00
|
|
|
$html .= '<div>';
|
|
|
|
$html .= '<label>'.$L->get('Show Content Stats').'</label>';
|
|
|
|
$html .= '<select name="showContentStats">';
|
|
|
|
$html .= '<option value="true" '.($this->getValue('showContentStats')===true?'selected':'').'>'.$L->get('Enabled').'</option>';
|
|
|
|
$html .= '<option value="false" '.($this->getValue('showContentStats')===false?'selected':'').'>'.$L->get('Disabled').'</option>';
|
|
|
|
$html .= '</select>';
|
|
|
|
$html .= '</div>';
|
|
|
|
|
2018-06-10 13:54:55 +02:00
|
|
|
if (defined('BLUDIT_PRO')) {
|
|
|
|
$html .= '<div>';
|
2018-08-05 17:54:20 +02:00
|
|
|
$html .= '<label>'.$L->get('Exclude administrators users').'</label>';
|
2018-06-10 13:54:55 +02:00
|
|
|
$html .= '<select name="excludeAdmins">';
|
2018-08-05 17:54:20 +02:00
|
|
|
$html .= '<option value="true" '.($this->getValue('excludeAdmins')===true?'selected':'').'>'.$L->get('Enabled').'</option>';
|
|
|
|
$html .= '<option value="false" '.($this->getValue('excludeAdmins')===false?'selected':'').'>'.$L->get('Disabled').'</option>';
|
2018-06-10 13:54:55 +02:00
|
|
|
$html .= '</select>';
|
|
|
|
$html .= '</div>';
|
|
|
|
}
|
|
|
|
|
|
|
|
return $html;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function adminHead()
|
|
|
|
{
|
2018-07-10 18:37:46 +02:00
|
|
|
if (!in_array($GLOBALS['ADMIN_CONTROLLER'], $this->loadOnController)) {
|
|
|
|
return false;
|
2018-06-10 13:54:55 +02:00
|
|
|
}
|
2018-07-10 18:37:46 +02:00
|
|
|
|
|
|
|
// Include plugin's CSS files
|
|
|
|
$html = $this->includeCSS('chartist.min.css');
|
|
|
|
$html .= $this->includeCSS('style.css');
|
|
|
|
|
|
|
|
// Include plugin's Javascript files
|
|
|
|
$html .= $this->includeJS('chartist.min.js');
|
|
|
|
|
|
|
|
return $html;
|
2018-06-10 13:54:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function dashboard()
|
|
|
|
{
|
2018-08-08 00:16:35 +02:00
|
|
|
global $L;
|
2018-06-10 13:54:55 +02:00
|
|
|
$label = $this->getValue('label');
|
|
|
|
$currentDate = Date::current('Y-m-d');
|
|
|
|
$visitsToday = $this->visits($currentDate);
|
|
|
|
$uniqueVisitors = $this->uniqueVisitors($currentDate);
|
|
|
|
|
|
|
|
$html = <<<EOF
|
|
|
|
<div class="simple-stats-plugin">
|
2018-09-03 14:45:13 +02:00
|
|
|
<div class="my-5 pt-4 border-top">
|
2018-08-11 12:53:06 +02:00
|
|
|
<h4 class="pb-3">{$L->get('Visits')}</h4>
|
2018-09-03 14:45:13 +02:00
|
|
|
<div class="ct-chart ct-perfect-fourth"></div>
|
|
|
|
<p class="legends visits-today">{$L->g('Visits today')}: $visitsToday</p>
|
|
|
|
<p class="legends unique-today">{$L->g('Unique visitors today')}: $uniqueVisitors</p>
|
2018-06-10 13:54:55 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
EOF;
|
2018-09-03 14:45:13 +02:00
|
|
|
|
2018-06-10 13:54:55 +02:00
|
|
|
$numberOfDays = $this->getValue('numberOfDays');
|
|
|
|
$numberOfDays = $numberOfDays - 1;
|
|
|
|
for ($i=$numberOfDays; $i >= 0 ; $i--) {
|
|
|
|
$dateWithOffset = Date::currentOffset('Y-m-d', '-'.$i.' day');
|
|
|
|
$visits[$i] = $this->visits($dateWithOffset);
|
|
|
|
$unique[$i] = $this->uniqueVisitors($dateWithOffset);
|
|
|
|
$days[$i] = Date::format($dateWithOffset, 'Y-m-d', 'D');
|
|
|
|
}
|
|
|
|
|
|
|
|
$labels = "'" . implode("','", $days) . "'";
|
|
|
|
$seriesVisits = implode(',', $visits);
|
|
|
|
$seriesUnique = implode(',', $unique);
|
|
|
|
|
|
|
|
$script = <<<EOF
|
|
|
|
<script>
|
|
|
|
var data = {
|
|
|
|
labels: [$labels],
|
|
|
|
series: [
|
|
|
|
[$seriesVisits],
|
|
|
|
[$seriesUnique]
|
|
|
|
]
|
|
|
|
};
|
|
|
|
|
|
|
|
var options = {
|
|
|
|
height: 250,
|
2018-09-03 21:04:31 +02:00
|
|
|
axisY: {
|
|
|
|
onlyInteger: true,
|
|
|
|
}
|
2018-06-10 13:54:55 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
new Chartist.Line('.ct-chart', data, options);
|
|
|
|
</script>
|
|
|
|
EOF;
|
|
|
|
|
|
|
|
$this->deleteOldLogs();
|
|
|
|
|
2018-09-03 14:45:13 +02:00
|
|
|
/**
|
|
|
|
* Optional Content Stats Feature
|
|
|
|
*/
|
|
|
|
if ($this->getValue('showContentStats')) {
|
|
|
|
|
|
|
|
global $pages, $categories, $tags;
|
|
|
|
|
|
|
|
$data['title'] = $L->get('Statistics');
|
|
|
|
$data['tabTitleChart'] = $L->get('Chart');
|
|
|
|
$data['tabTitleTable'] = $L->get('Table');
|
|
|
|
$data['data'][$L->get('published')] = count($pages->getPublishedDB());
|
|
|
|
$data['data'][$L->get('static')] = count($pages->getStaticDB());
|
|
|
|
$data['data'][$L->get('drafts')] = count($pages->getDraftDB());
|
|
|
|
$data['data'][$L->get('scheduled')] = count($pages->getScheduledDB());
|
|
|
|
$data['data'][$L->get('sticky')] = count($pages->getStickyDB());
|
|
|
|
$data['data'][$L->get('categories')]= count($categories->keys());
|
|
|
|
$data['data'][$L->get('tags')] = count($tags->keys());
|
|
|
|
$html .= $this->renderContentStatistics($data);
|
|
|
|
}
|
|
|
|
|
2018-06-10 13:54:55 +02:00
|
|
|
return $html.PHP_EOL.$script.PHP_EOL;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function siteBodyEnd()
|
|
|
|
{
|
|
|
|
$this->addVisitor();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep only 7 days of logs, remove the old ones
|
|
|
|
public function deleteOldLogs()
|
|
|
|
{
|
|
|
|
$logs = Filesystem::listFiles($this->workspace(), '*', 'log', true);
|
|
|
|
$remove = array_slice($logs, 7);
|
|
|
|
|
|
|
|
foreach ($remove as $log) {
|
|
|
|
Filesystem::rmfile($log);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the amount of visits by date
|
|
|
|
public function visits($date)
|
|
|
|
{
|
|
|
|
$file = $this->workspace().$date.'.log';
|
|
|
|
$handle = @fopen($file, 'rb');
|
|
|
|
if ($handle===false) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The amount of visits are the number of lines on the file
|
|
|
|
$lines = 0;
|
|
|
|
while (!feof($handle)) {
|
|
|
|
$lines += substr_count(fread($handle, 8192), PHP_EOL);
|
|
|
|
}
|
|
|
|
@fclose($handle);
|
|
|
|
return $lines;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the amount of unique visitors by date
|
|
|
|
public function uniqueVisitors($date)
|
|
|
|
{
|
|
|
|
$file = $this->workspace().$date.'.log';
|
|
|
|
$lines = @file($file);
|
|
|
|
if (empty($lines)) {
|
|
|
|
return 0;
|
|
|
|
}
|
2018-08-19 17:45:49 +02:00
|
|
|
|
2018-06-10 13:54:55 +02:00
|
|
|
$tmp = array();
|
|
|
|
foreach ($lines as $line) {
|
2018-07-25 23:42:00 +02:00
|
|
|
$data = json_decode($line);
|
|
|
|
$hashIP = $data[0];
|
|
|
|
$tmp[$hashIP] = true;
|
2018-06-10 13:54:55 +02:00
|
|
|
}
|
|
|
|
return count($tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a line to the current log
|
|
|
|
// The line is a json array with the hash IP of the visitor and the time
|
|
|
|
public function addVisitor()
|
|
|
|
{
|
2018-07-25 23:42:00 +02:00
|
|
|
if (Cookie::get('BLUDIT-KEY') && defined('BLUDIT_PRO') && $this->getValue('excludeAdmins')) {
|
|
|
|
return false;
|
2018-06-10 13:54:55 +02:00
|
|
|
}
|
|
|
|
$currentTime = Date::current('Y-m-d H:i:s');
|
|
|
|
$ip = TCP::getIP();
|
|
|
|
$hashIP = md5($ip);
|
|
|
|
|
|
|
|
$line = json_encode(array($hashIP, $currentTime));
|
|
|
|
$currentDate = Date::current('Y-m-d');
|
2018-07-25 23:42:00 +02:00
|
|
|
$logFile = $this->workspace().$currentDate.'.log';
|
2018-06-10 13:54:55 +02:00
|
|
|
|
2018-07-25 23:42:00 +02:00
|
|
|
return file_put_contents($logFile, $line.PHP_EOL, FILE_APPEND | LOCK_EX)!==false;
|
2018-06-10 13:54:55 +02:00
|
|
|
}
|
|
|
|
|
2018-09-03 14:45:13 +02:00
|
|
|
public function renderContentStatistics($data)
|
|
|
|
{
|
|
|
|
$html = '<div class="my-5 pt-4 border-top">';
|
|
|
|
$html .= "<h4 class='pb-2'>{$data['title']}</h4>";
|
|
|
|
$html .= '
|
|
|
|
<nav>
|
|
|
|
<div class="nav nav-tabs" id="nav-tab" role="tablist">
|
|
|
|
<a class="nav-item nav-link active" id="nav-stats-chart-tab" data-toggle="tab" href="#nav-stats-chart" role="tab" aria-controls="nav-stats-chart" aria-selected="true">' . $data['tabTitleChart'] .'</a>
|
|
|
|
<a class="nav-item nav-link" id="nav-stats-table-tab" data-toggle="tab" href="#nav-stats-table" role="tab" aria-controls="nav-stats-table" aria-selected="false">' . $data['tabTitleTable'] .'</a>
|
|
|
|
</div>
|
|
|
|
</nav>
|
|
|
|
<div class="tab-content my-2" id="nav-tabContent">
|
|
|
|
<div class="tab-pane fade show active" id="nav-stats-chart" role="tabpanel" aria-labelledby="nav-stats-chart-tab">
|
|
|
|
<div class="ct-chart-content pt-2"></div>
|
|
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="nav-stats-table" role="tabpanel" aria-labelledby="nav-stats-table-tab">
|
|
|
|
<table class="table table-borderless table-sm table-striped mt-3">
|
|
|
|
<tbody>';
|
|
|
|
|
|
|
|
foreach ($data['data'] as $th => $td) {
|
|
|
|
$html .= "
|
|
|
|
<tr>
|
|
|
|
<th>$th</th>
|
|
|
|
<td>$td</td>
|
|
|
|
</tr>
|
|
|
|
";
|
|
|
|
}
|
|
|
|
|
|
|
|
$html .= '
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
new Chartist.Bar(".ct-chart-content", {
|
|
|
|
labels: ' . json_encode(array_keys($data['data'])) . ',
|
|
|
|
series: ' . json_encode(array_values($data['data'])) . '
|
|
|
|
}, {
|
|
|
|
distributeSeries: true
|
|
|
|
});
|
|
|
|
</script>';
|
|
|
|
|
|
|
|
return $html;
|
|
|
|
}
|
|
|
|
|
2018-08-11 12:53:06 +02:00
|
|
|
}
|