forked from vergnet/site-accueil-insa
460 lines
18 KiB
PHP
460 lines
18 KiB
PHP
<?php
|
|
/**
|
|
* Matomo - free/libre analytics platform
|
|
*
|
|
* @link https://matomo.org
|
|
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
|
*
|
|
*/
|
|
|
|
namespace Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines;
|
|
use Piwik\Common;
|
|
use Piwik\DataTable\Filter\CalculateEvolutionFilter;
|
|
use Piwik\Metrics;
|
|
use Piwik\NoAccessException;
|
|
use Piwik\Period\Range;
|
|
use Piwik\Site;
|
|
use Piwik\Url;
|
|
|
|
/**
|
|
* DataTable Visualization that derives from Sparklines.
|
|
*/
|
|
class Config extends \Piwik\ViewDataTable\Config
|
|
{
|
|
/**
|
|
* Holds metrics / column names that will be used to fetch data from the configured $requestConfig API.
|
|
* Default value: array
|
|
*/
|
|
private $sparkline_metrics = array();
|
|
|
|
/**
|
|
* Holds the actual sparkline entries based on fetched data that will be used in the template.
|
|
* @var array
|
|
*/
|
|
private $sparklines = array();
|
|
|
|
/**
|
|
* If false, will not link them with any evolution graph
|
|
* @var bool
|
|
*/
|
|
private $evolutionGraphLinkable = true;
|
|
|
|
/**
|
|
* Adds possibility to set html attributes on the sparklines title / headline. For example can be used
|
|
* to set an angular directive
|
|
* @var string
|
|
*/
|
|
public $title_attributes = array();
|
|
|
|
/**
|
|
* Defines custom parameters that will be appended to the sparkline image urls
|
|
*/
|
|
public $custom_parameters = [];
|
|
|
|
/**
|
|
* If supplied, this function is used to compute the evolution percent displayed next to non-comparison sparkline views.
|
|
*
|
|
* The function is passed three parameters:
|
|
* - an array mapping column names with column values ['column' => 123]
|
|
* - an array of \Piwik\Plugin\Metrics objects available for the report - useful for formatting values
|
|
*
|
|
* compute_evolution(array, array)
|
|
*
|
|
* @var callable
|
|
*/
|
|
public $compute_evolution = null;
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
|
|
$this->translations = Metrics::getDefaultMetricTranslations();
|
|
}
|
|
|
|
/**
|
|
* @ignore
|
|
* @return array
|
|
*/
|
|
public function getSparklineMetrics()
|
|
{
|
|
return $this->sparkline_metrics;
|
|
}
|
|
|
|
/**
|
|
* @ignore
|
|
* @return bool
|
|
*/
|
|
public function hasSparklineMetrics()
|
|
{
|
|
return !empty($this->sparkline_metrics);
|
|
}
|
|
|
|
/**
|
|
* Removes an existing sparkline entry. Especially useful in dataTable filters in case sparklines should be not
|
|
* displayed depending on the fetched data.
|
|
*
|
|
* Example:
|
|
* $config->addSparklineMetric('nb_users');
|
|
* $config->filters[] = function ($dataTable) use ($config) {
|
|
* if ($dataTable->getFirstRow()->getColumn('nb_users') == 0) {
|
|
* // do not show a sparkline if there are no recorded users
|
|
* $config->removeSparklineMetric('nb_users');
|
|
* }
|
|
* }
|
|
*
|
|
* @param array|string $metricNames The name of the metrics in the same format they were used when added via
|
|
* {@link addSparklineMetric}
|
|
*/
|
|
public function removeSparklineMetric($metricNames)
|
|
{
|
|
foreach ($this->sparkline_metrics as $index => $metric) {
|
|
if ($metric['columns'] === $metricNames) {
|
|
array_splice($this->sparkline_metrics, $index, 1);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Replaces an existing sparkline entry with different columns. Especially useful in dataTable filters in case
|
|
* sparklines should be not displayed depending on the fetched data.
|
|
*
|
|
* Example:
|
|
* $config->addSparklineMetric('nb_users');
|
|
* $config->filters[] = function ($dataTable) use ($config) {
|
|
* if ($dataTable->getFirstRow()->getColumn('nb_users') == 0) {
|
|
* // instead of showing the sparklines for users, show a placeholder if there are no recorded users
|
|
* $config->replaceSparklineMetric(array('nb_users'), '');
|
|
* }
|
|
* }
|
|
*
|
|
* @param array|string $metricNames The name of the metrics in the same format they were used when added via
|
|
* {@link addSparklineMetric}
|
|
* @param array|string $replacementColumns The removed columns will be replaced with these columns
|
|
*/
|
|
public function replaceSparklineMetric($metricNames, $replacementColumns)
|
|
{
|
|
foreach ($this->sparkline_metrics as $index => $metric) {
|
|
if ($metric['columns'] === $metricNames) {
|
|
$this->sparkline_metrics[$index]['columns'] = $replacementColumns;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a new sparkline.
|
|
*
|
|
* It will show a sparkline image, the value of the resolved metric name and a descrption. Optionally, multiple
|
|
* values can be shown after a sparkline image by passing multiple metric names
|
|
* (eg array('nb_visits', 'nb_actions')). The data will be requested from the configured api method see
|
|
* {@link \Piwik\ViewDataTable\RequestConfig::$apiMethodToRequestDataTable}.
|
|
*
|
|
* Example:
|
|
* $config->addSparklineMetric('nb_visits');
|
|
* $config->addTranslation('nb_visits', 'Visits');
|
|
* Results in: [sparkline image] X visits
|
|
*
|
|
* Example:
|
|
* $config->addSparklineMetric(array('nb_visits', 'nb_actions'));
|
|
* $config->addTranslations(array('nb_visits' => 'Visits', 'nb_actions' => 'Actions'));
|
|
* Results in: [sparkline image] X visits, Y actions
|
|
*
|
|
* @param string|array $metricName Either one metric name (eg 'nb_visits') or an array of metric names
|
|
* @param int|null $order Defines the order. The lower the order the earlier the sparkline will be displayed.
|
|
* By default the sparkline will be appended to the end.
|
|
* @param array $graphParams The params to use when changing an associated evolution graph. By default this is determined
|
|
* from the sparkline URL, but sometimes the sparkline API method may not match the evolution graph API method.
|
|
*/
|
|
public function addSparklineMetric($metricName, $order = null, $graphParams = null)
|
|
{
|
|
$this->sparkline_metrics[] = array(
|
|
'columns' => $metricName,
|
|
'order' => $order,
|
|
'graphParams' => $graphParams,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Adds a placeholder. In this case nothing will be shown, neither a sparkline nor any description. This can be
|
|
* useful if you want to have some kind of separator. Eg if you want to have a sparkline on the left side but
|
|
* not sparkline on the right side.
|
|
*
|
|
* @param int|null $order Defines the order. The lower the order the earlier the sparkline will be displayed.
|
|
* By default the sparkline will be appended to the end.
|
|
*/
|
|
public function addPlaceholder($order = null)
|
|
{
|
|
$this->sparklines[] = array(
|
|
'url' => '',
|
|
'metrics' => array(),
|
|
'order' => $this->getSparklineOrder($order),
|
|
|
|
// adding this group ensures the sparkline will be placed between individual sparklines, and not in their own group together
|
|
'group' => 'placeholder' . count($this->sparklines),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Add a new sparkline to be displayed to the view.
|
|
*
|
|
* Each sparkline can consist of one or multiple metrics. One metric consists of a value and a description. By
|
|
* default the value is shown first, then the description. The description can optionally contain a '%s' in case
|
|
* the value shall be displayed within the description. If multiple metrics are given, they will be separated by
|
|
* a comma.
|
|
*
|
|
* @param array $requestParamsForSparkline You need to at least set a module / action eg
|
|
* array('columns' => array('nb_visit'), 'module' => '', 'action' => '')
|
|
* @param int|float|string|array $value Either the metric value or an array of values.
|
|
* @param string|array $description Either one description or an array of descriptions. If an array, both
|
|
* $value and $description need the same amount of array entries.
|
|
* $description[0] should be the description for $value[0].
|
|
* $description should be already translated. If $value should appear
|
|
* somewhere within the text a `%s` can be used in the translation.
|
|
* @param array|null $evolution Optional array containing at least the array keys 'currentValue' and
|
|
* 'pastValue' which are needed to calculate the correct percentage.
|
|
* An optional 'tooltip' can be set as well. Eg
|
|
* array('currentValue' => 10, 'pastValue' => 20,
|
|
* 'tooltip' => '10 visits in 2015-07-26 compared to 20 visits in 2015-07-25')
|
|
* @param int $order Defines the order. The lower the order the earlier the sparkline will be
|
|
* displayed. By default the sparkline will be appended to the end.
|
|
* @param string $title The title of this specific sparkline. It is displayed on the left above the sparkline image.
|
|
* @param string $group The ID of the group for this sparkline.
|
|
* @param int $seriesIndices The indexes of each series displayed in the sparkline. This determines what color is used for each series. Mainly used for comparison.
|
|
* @param array $graphParams The params to use when changing an associated evolution graph. By default this is determined
|
|
* from the sparkline URL, but sometimes the sparkline API method may not match the evolution graph API method.
|
|
* @throws \Exception In case an evolution parameter is set but has wrong data structure
|
|
*/
|
|
public function addSparkline($requestParamsForSparkline, $metricInfos, $description, $evolution = null, $order = null, $title = null, $group = '', $seriesIndices = null, $graphParams = null)
|
|
{
|
|
$metrics = array();
|
|
|
|
if ($description === null && is_array($metricInfos)) {
|
|
$metrics = $metricInfos;
|
|
} else {
|
|
$value = $metricInfos;
|
|
|
|
if (is_array($value)) {
|
|
$values = $value;
|
|
} else {
|
|
$values = array($value);
|
|
}
|
|
|
|
if (!is_array($description)) {
|
|
$description = array($description);
|
|
}
|
|
|
|
if (count($values) === count($description)) {
|
|
foreach ($values as $index => $value) {
|
|
$metrics[] = array(
|
|
'value' => $value,
|
|
'description' => $description[$index]
|
|
);
|
|
}
|
|
} else {
|
|
$msg = 'The number of values and descriptions need to be the same to add a sparkline. ';
|
|
$msg .= 'Values: ' . implode(', ', $values). ' Descriptions: ' . implode(', ', $description);
|
|
throw new \Exception($msg);
|
|
}
|
|
}
|
|
|
|
if (!empty($requestParamsForSparkline['columns'])
|
|
&& is_array($requestParamsForSparkline['columns'])
|
|
&& count($requestParamsForSparkline['columns']) === count($metrics)) {
|
|
$columns = array_values($requestParamsForSparkline['columns']);
|
|
} elseif (!empty($requestParamsForSparkline['columns'])
|
|
&& is_string($requestParamsForSparkline['columns'])
|
|
&& count($metrics) === 1) {
|
|
$columns = array($requestParamsForSparkline['columns']);
|
|
} else{
|
|
$columns = array();
|
|
}
|
|
|
|
foreach ($metrics as $index => $metricInfo) {
|
|
$metrics[$index]['column'] = isset($columns[$index]) ? $columns[$index] : '';
|
|
}
|
|
|
|
if (empty($metrics)) {
|
|
return;
|
|
}
|
|
|
|
$groupedMetrics = [];
|
|
foreach ($metrics as $metricInfo) {
|
|
$metricGroup = isset($metricInfo['group']) ? $metricInfo['group'] : '';
|
|
$groupedMetrics[$metricGroup][] = $metricInfo;
|
|
}
|
|
|
|
$sparkline = array(
|
|
'url' => $this->getUrlSparkline($requestParamsForSparkline),
|
|
'metrics' => $groupedMetrics,
|
|
'order' => $this->getSparklineOrder($order),
|
|
'title' => $title,
|
|
'group' => $group,
|
|
'seriesIndices' => $seriesIndices,
|
|
'graphParams' => $graphParams,
|
|
);
|
|
|
|
if (!empty($evolution)) {
|
|
if (!is_array($evolution) ||
|
|
!array_key_exists('currentValue', $evolution) ||
|
|
!array_key_exists('pastValue', $evolution)
|
|
) {
|
|
throw new \Exception('In order to show an evolution in the sparklines view a currentValue and pastValue array key needs to be present');
|
|
}
|
|
|
|
$evolutionPercent = CalculateEvolutionFilter::calculate($evolution['currentValue'], $evolution['pastValue'], $precision = 1);
|
|
|
|
// do not display evolution if evolution percent is 0 and current value is 0
|
|
if ($evolutionPercent != 0 || $evolution['currentValue'] != 0) {
|
|
$sparkline['evolution'] = array(
|
|
'percent' => $evolutionPercent,
|
|
'tooltip' => !empty($evolution['tooltip']) ? $evolution['tooltip'] : null,
|
|
'trend' => $evolution['currentValue'] - $evolution['pastValue'],
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
$this->sparklines[] = $sparkline;
|
|
}
|
|
|
|
/**
|
|
* If there are sparklines and evolution graphs on one page, we try to connect them so that when you click on a
|
|
* sparkline, the evolution graph will update and show the evolution for that sparkline metric. In some cases
|
|
* we might falsely connect sparklines with an evolution graph that don't belong together. In this case you can
|
|
* mark all sparklines as "not linkable". This will prevent the sparklines being linked with an evolution graph.
|
|
*/
|
|
public function setNotLinkableWithAnyEvolutionGraph()
|
|
{
|
|
$this->evolutionGraphLinkable = false;
|
|
}
|
|
|
|
/**
|
|
* Detect whether sparklines are linkable with an evolution graph. {@link setNotLinkableWithAnyEvolutionGraph()}
|
|
*/
|
|
public function areSparklinesLinkable()
|
|
{
|
|
return $this->evolutionGraphLinkable;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
* @ignore
|
|
*/
|
|
public function getSortedSparklines()
|
|
{
|
|
usort($this->sparklines, function ($a, $b) {
|
|
if ($a['order'] == $b['order']) {
|
|
return 0;
|
|
}
|
|
return ($a['order'] < $b['order']) ? -1 : 1;
|
|
});
|
|
|
|
$sparklines = [];
|
|
foreach ($this->sparklines as $sparkline) {
|
|
$group = $sparkline['group'];
|
|
$sparklines[$group][] = $sparkline;
|
|
}
|
|
return $sparklines;
|
|
}
|
|
|
|
private function getSparklineOrder($order)
|
|
{
|
|
if (!isset($order)) {
|
|
// make sure to append to the end if nothing set (in the order they are added)
|
|
$order = 999 + count($this->sparklines);
|
|
}
|
|
|
|
return (int) $order;
|
|
}
|
|
|
|
/**
|
|
* Returns a URL to a sparkline image for a report served by the current plugin.
|
|
*
|
|
* The result of this URL should be used with the [sparkline()](/api-reference/Piwik/View#twig) twig function.
|
|
*
|
|
* The current site ID and period will be used.
|
|
*
|
|
* @param array $customParameters The array of query parameter name/value pairs that
|
|
* should be set in result URL.
|
|
* @return string The generated URL.
|
|
*/
|
|
private function getUrlSparkline($customParameters = array())
|
|
{
|
|
$customParameters['viewDataTable'] = 'sparkline';
|
|
|
|
$params = $this->getGraphParamsModified($customParameters);
|
|
|
|
// convert array values to comma separated
|
|
foreach ($params as &$value) {
|
|
if (is_array($value)) {
|
|
$value = rawurlencode(implode(',', $value));
|
|
}
|
|
}
|
|
$url = Url::getCurrentQueryStringWithParametersModified($params);
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* Returns the array of new processed parameters once the parameters are applied.
|
|
* For example: if you set range=last30 and date=2008-03-10,
|
|
* the date element of the returned array will be "2008-02-10,2008-03-10"
|
|
*
|
|
* Parameters you can set:
|
|
* - range: last30, previous10, etc.
|
|
* - date: YYYY-MM-DD, today, yesterday
|
|
* - period: day, week, month, year
|
|
*
|
|
* @param array $paramsToSet array( 'date' => 'last50', 'viewDataTable' =>'sparkline' )
|
|
* @throws \Piwik\NoAccessException
|
|
* @return array
|
|
*/
|
|
private function getGraphParamsModified($paramsToSet = array())
|
|
{
|
|
if (!isset($paramsToSet['period'])) {
|
|
$period = Common::getRequestVar('period');
|
|
} else {
|
|
$period = $paramsToSet['period'];
|
|
}
|
|
|
|
if ($period == 'range') {
|
|
return $paramsToSet;
|
|
}
|
|
|
|
if (!isset($paramsToSet['range'])) {
|
|
$range = 'last30';
|
|
} else {
|
|
$range = $paramsToSet['range'];
|
|
}
|
|
|
|
if (!isset($paramsToSet['idSite'])) {
|
|
$idSite = Common::getRequestVar('idSite');
|
|
} else {
|
|
$idSite = $paramsToSet['idSite'];
|
|
}
|
|
|
|
if (!isset($paramsToSet['date'])) {
|
|
$endDate = Common::getRequestVar('date', 'yesterday', 'string');
|
|
} else {
|
|
$endDate = $paramsToSet['date'];
|
|
}
|
|
|
|
$site = new Site($idSite);
|
|
|
|
if (is_null($site)) {
|
|
throw new NoAccessException("Website not initialized, check that you are logged in and/or using the correct token_auth.");
|
|
}
|
|
|
|
if (!isset($paramsToSet['date'])
|
|
|| !Range::isMultiplePeriod($paramsToSet['date'], $period)
|
|
) {
|
|
$paramDate = Range::getRelativeToEndDate($period, $range, $endDate, $site);
|
|
} else {
|
|
$paramDate = $paramsToSet['date'];
|
|
}
|
|
|
|
$params = array_merge($paramsToSet, array('date' => $paramDate));
|
|
return $params;
|
|
}
|
|
|
|
}
|