PDF rausgenommen

This commit is contained in:
aschwarz
2023-01-23 11:03:31 +01:00
parent 82d562a322
commit a6523903eb
28078 changed files with 4247552 additions and 2 deletions

View File

@ -0,0 +1,198 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Plugin\Visualization;
use Piwik\View;
/**
* Generates a tag cloud from a given data array.
* The generated tag cloud can be in PHP format, or in HTML.
*
* Inspired from Derek Harvey (www.derekharvey.co.uk)
*
* @property Cloud\Config $config
*
*/
class Cloud extends Visualization
{
const ID = 'cloud';
const TEMPLATE_FILE = "@CoreVisualizations/_dataTableViz_tagCloud.twig";
const FOOTER_ICON = 'icon-tag-cloud';
const FOOTER_ICON_TITLE = 'General_TagCloud';
/** Used by system tests to make sure output is consistent. */
public static $debugDisableShuffle = false;
public $truncatingLimit = 50;
protected $wordsArray = array();
public static function getDefaultConfig()
{
return new Cloud\Config();
}
public function beforeRender()
{
$this->config->show_exclude_low_population = false;
$this->config->show_offset_information = false;
$this->config->show_limit_control = false;
}
public function afterAllFiltersAreApplied()
{
if ($this->dataTable->getRowsCount() == 0) {
return;
}
$columnToDisplay = isset($this->config->columns_to_display[1]) ? $this->config->columns_to_display[1] : 'nb_visits';
$labelMetadata = array();
foreach ($this->dataTable->getRows() as $row) {
$logo = false;
if ($this->config->display_logo_instead_of_label) {
$logo = $row->getMetadata('logo');
}
$label = $row->getColumn('label');
$labelMetadata[$label] = array(
'logo' => $logo,
'url' => $row->getMetadata('url'),
);
$this->addWord($label, $row->getColumn($columnToDisplay));
}
$cloudValues = $this->getCloudValues();
foreach ($cloudValues as &$value) {
$value['logoWidth'] = round(max(16, $value['percent']));
}
$this->assignTemplateVar('labelMetadata', $labelMetadata);
$this->assignTemplateVar('cloudColumn', $columnToDisplay);
$this->assignTemplateVar('cloudValues', $cloudValues);
}
/**
* Assign word to array
* @param string $word
* @param int $value
* @return string
*/
public function addWord($word, $value = 1)
{
if (isset($this->wordsArray[$word])) {
$this->wordsArray[$word] += $value;
} else {
$this->wordsArray[$word] = $value;
}
}
private function getCloudValues()
{
$this->shuffleCloud();
if (empty($this->wordsArray)) {
return array();
}
$return = array();
$maxValue = max($this->wordsArray);
foreach ($this->wordsArray as $word => $popularity) {
$wordTruncated = $this->truncateWordIfNeeded($word);
$percent = $this->getPercentage($popularity, $maxValue);
$sizeRange = $this->getClassFromPercent($percent);
$return[$word] = array(
'word' => $word,
'wordTruncated' => $wordTruncated,
'value' => $popularity,
'size' => $sizeRange,
'percent' => $percent,
);
}
return $return;
}
/**
* Shuffle associated names in array
*/
protected function shuffleCloud()
{
if (self::$debugDisableShuffle) {
return;
}
$keys = array_keys($this->wordsArray);
shuffle($keys);
if (count($keys) && is_array($keys)) {
$tmpArray = $this->wordsArray;
$this->wordsArray = array();
foreach ($keys as $value) {
$this->wordsArray[$value] = $tmpArray[$value];
}
}
}
/**
* Get the class range using a percentage
*
* @param $percent
*
* @return int class
*/
protected function getClassFromPercent($percent)
{
$mapping = array(95, 70, 50, 30, 15, 5, 0);
foreach ($mapping as $key => $value) {
if ($percent >= $value) {
return $key;
}
}
return 0;
}
/**
* @param $word
* @return string
*/
private function truncateWordIfNeeded($word)
{
$word = Common::unsanitizeInputValue($word);
if (Common::mb_strlen($word) > $this->truncatingLimit) {
return Common::mb_substr($word, 0, $this->truncatingLimit - 3) . '...';
}
return $word;
}
private function getPercentage($popularity, $maxValue)
{
// case hideFutureHoursWhenToday=1 shows hours with no visits
if ($maxValue == 0) {
return 0;
}
$percent = ($popularity / $maxValue) * 100;
return $percent;
}
}

View File

@ -0,0 +1,35 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\Cloud;
use Piwik\ViewDataTable\Config as VisualizationConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class Config extends VisualizationConfig
{
/**
* Whether to display the logo assocatied with a DataTable row (stored as 'logo' row metadata)
* instead of the label in Tag Clouds.
*
* Default value: false
*/
public $display_logo_instead_of_label = false;
public function __construct()
{
parent::__construct();
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array('display_logo_instead_of_label'));
}
}

View File

@ -0,0 +1,160 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Plugins\CoreVisualizations\Metrics\Formatter\Numeric;
use Piwik\Piwik;
use Piwik\Plugin\Visualization;
/**
* This is an abstract visualization that should be the base of any 'graph' visualization.
* This class defines certain visualization properties that are specific to all graph types.
* Derived visualizations can decide for themselves whether they should support individual
* properties.
*
* @property Graph\Config $config
*/
abstract class Graph extends Visualization
{
const ID = 'graph';
public $selectableRows = array();
public static function getDefaultConfig()
{
return new Graph\Config();
}
public static function getDefaultRequestConfig()
{
$config = parent::getDefaultRequestConfig();
$config->addPropertiesThatShouldBeAvailableClientSide(array('columns'));
return $config;
}
public function beforeRender()
{
if ($this->config->show_goals) {
$this->config->translations['nb_conversions'] = Piwik::translate('Goals_ColumnConversions');
$this->config->translations['revenue'] = Piwik::translate('General_TotalRevenue');
}
}
public function beforeLoadDataTable()
{
// TODO: this should not be required here. filter_limit should not be a view property, instead HtmlTable should use 'limit' or something,
// and manually set request_parameters_to_modify['filter_limit'] based on that. (same for filter_offset).
$this->requestConfig->request_parameters_to_modify['filter_limit'] = false;
if ($this->config->max_graph_elements) {
$this->requestConfig->request_parameters_to_modify['filter_truncate'] = $this->config->max_graph_elements - 1;
}
$this->requestConfig->request_parameters_to_modify['format_metrics'] = 1;
$this->metricsFormatter = new Numeric();
}
/**
* Determines what rows are selectable and stores them in the selectable_rows property in
* a format the SeriesPicker JavaScript class can use.
*/
public function determineWhichRowsAreSelectable()
{
if ($this->config->row_picker_match_rows_by === false) {
return;
}
// collect all selectable rows
$self = $this;
$this->dataTable->filter(function ($dataTable) use ($self) {
/** @var DataTable $dataTable */
foreach ($dataTable->getRows() as $row) {
$rowLabel = $row->getColumn('label');
if (false === $rowLabel) {
continue;
}
// build config
if (!isset($self->selectableRows[$rowLabel])) {
$self->selectableRows[$rowLabel] = array(
'label' => $rowLabel,
'matcher' => $rowLabel,
'displayed' => $self->isRowVisible($rowLabel)
);
}
}
});
}
public function isRowVisible($rowLabel)
{
$isVisible = true;
if ('label' == $this->config->row_picker_match_rows_by) {
$isVisible = in_array($rowLabel, $this->config->rows_to_display === false ? [] : $this->config->rows_to_display);
}
return $isVisible;
}
/**
* Defaults the selectable_columns property if it has not been set and then transforms
* it into something the SeriesPicker JavaScript class can use.
*/
public function afterAllFiltersAreApplied()
{
$this->determineWhichRowsAreSelectable();
$this->config->selectable_rows = array_values($this->selectableRows);
if ($this->config->add_total_row) {
$totalTranslation = Piwik::translate('General_Total');
$this->config->selectable_rows[] = array(
'label' => $totalTranslation,
'matcher' => $totalTranslation,
'displayed' => $this->isRowVisible($totalTranslation)
);
}
if ($this->config->show_goals) {
$this->config->addTranslations(array(
'nb_conversions' => Piwik::translate('Goals_ColumnConversions'),
'revenue' => Piwik::translate('General_TotalRevenue')
));
}
// set default selectable columns, if none specified
$selectableColumns = $this->config->selectable_columns;
if (false === $selectableColumns) {
$selectableColumns = array('nb_visits', 'nb_actions', 'nb_uniq_visitors', 'nb_users');
if ($this->config->show_goals) {
$goalMetrics = array('nb_conversions', 'revenue');
$selectableColumns = array_merge($selectableColumns, $goalMetrics);
}
}
$transformed = array();
foreach ($selectableColumns as $column) {
$transformed[] = array(
'column' => $column,
'translation' => @$this->config->translations[$column],
'displayed' => in_array($column, $this->config->columns_to_display)
);
}
$this->config->selectable_columns = $transformed;
}
}

View File

@ -0,0 +1,122 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\Graph;
use Piwik\ViewDataTable\Config as VisualizationConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class Config extends VisualizationConfig
{
/**
* Whether the series picker should allow picking more than one series or not.
*
* Default value: true
*/
public $allow_multi_select_series_picker = true;
/**
* The maximum number of rows to render. All other rows will be aggregated in an 'Others' row.
*
* Default value: false (no limit)
*/
public $max_graph_elements = false;
/**
* Array property that contains the names of columns that can be selected in the Series Picker.
*
* Default value: false
*/
public $selectable_columns = false;
/**
* Contains the column (if any) of the values used in the Row Picker.
*
* @see self::ROWS_TO_DISPLAY
*
* Default value: false
*/
public $row_picker_match_rows_by = false;
/**
* Contains the list of values identifying rows that should be displayed as separate series.
* The values are of a specific column determined by the row_picker_match_rows_by column.
*
* @see self::ROW_PICKER_VALUE_COLUMN
*
* Default value: false
*/
public $rows_to_display = false;
/**
* Contains the list of values available for the Row Picker. Currently set to be all visible
* rows, if the row_picker_match_rows_by property is set.
*
* @see self::ROW_PICKER_VALUE_COLUMN
*/
public $selectable_rows = 'selectable_rows';
/**
* Controls whether all ticks & labels are shown on a graph's x-axis or just some.
*
* Default value: false
*/
public $show_all_ticks = false;
/**
* If true, a row with totals of each DataTable column is added.
*
* Default value: false
*/
public $add_total_row = false;
/**
* Controls whether the Series Picker is shown or not. The Series Picker allows users to
* choose between displaying data of different columns.
*
* Default value: true
*/
public $show_series_picker = true;
/**
* Controls whether the percentage of the total is displayed as a tooltip when hovering over
* data points.
*
* NOTE: Sometimes this percentage is meaningless (when the total of the column values is
* not the total number of elements in the set). In this case the tooltip should not be
* displayed.
*
* Default value: true
*/
public $display_percentage_in_tooltip = true;
public function __construct()
{
parent::__construct();
$this->show_limit_control = false;
$this->addPropertiesThatShouldBeAvailableClientSide(array(
'show_series_picker',
'allow_multi_select_series_picker',
'selectable_columns',
'selectable_rows',
'display_percentage_in_tooltip'
));
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array(
'show_all_ticks',
'show_series_picker'
));
}
}

View File

@ -0,0 +1,120 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
use Piwik\API\Request as ApiRequest;
use Piwik\Common;
use Piwik\DataTable\Row;
use Piwik\Metrics;
use Piwik\Period;
use Piwik\Plugin\Visualization;
/**
* DataTable visualization that shows DataTable data in an HTML table.
*
* @property HtmlTable\Config $config
*/
class HtmlTable extends Visualization
{
const ID = 'table';
const TEMPLATE_FILE = "@CoreVisualizations/_dataTableViz_htmlTable.twig";
const FOOTER_ICON = 'icon-table';
const FOOTER_ICON_TITLE = 'General_DisplaySimpleTable';
public static function getDefaultConfig()
{
return new HtmlTable\Config();
}
public static function getDefaultRequestConfig()
{
return new HtmlTable\RequestConfig();
}
public function beforeRender()
{
if ($this->requestConfig->idSubtable
&& $this->config->show_embedded_subtable) {
$this->config->show_visualization_only = true;
}
if ($this->requestConfig->idSubtable) {
$this->config->show_totals_row = false;
}
foreach (Metrics::getMetricIdsToProcessReportTotal() as $metricId) {
$this->config->report_ratio_columns[] = Metrics::getReadableColumnName($metricId);
}
if (!empty($this->report)) {
foreach ($this->report->getMetricNamesToProcessReportTotals() as $metricName) {
$this->config->report_ratio_columns[] = $metricName;
}
}
// we do not want to get a datatable\map
$period = Common::getRequestVar('period', 'day', 'string');
if (Period\Range::parseDateRange($period)) {
$period = 'range';
}
if ($this->dataTable->getRowsCount()) {
$request = new ApiRequest(array(
'method' => 'API.get',
'module' => 'API',
'action' => 'get',
'format' => 'original',
'filter_limit' => '-1',
'disable_generic_filters' => 1,
'expanded' => 0,
'flat' => 0,
'filter_offset' => 0,
'period' => $period,
'showColumns' => implode(',', $this->config->columns_to_display),
'columns' => implode(',', $this->config->columns_to_display),
'pivotBy' => ''
));
$dataTable = $request->process();
$this->assignTemplateVar('siteSummary', $dataTable);
}
if ($this->isPivoted()) {
$this->config->columns_to_display = $this->dataTable->getColumns();
}
}
public function beforeGenericFiltersAreAppliedToLoadedDataTable()
{
if ($this->isPivoted()) {
$this->config->columns_to_display = $this->dataTable->getColumns();
$this->dataTable->applyQueuedFilters();
}
parent::beforeGenericFiltersAreAppliedToLoadedDataTable();
}
protected function isPivoted()
{
return $this->requestConfig->pivotBy || Common::getRequestVar('pivotBy', '');
}
/**
* Override to compute a custom cell HTML attributes (such as style).
*
* @param Row $row
* @param $column
* @return array Array of name => value pairs.
*/
public function getCellHtmlAttributes(Row $row, $column)
{
return null;
}
}

View File

@ -0,0 +1,78 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
use Piwik\DataTable;
use Piwik\Metrics;
use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
use Piwik\View;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class AllColumns extends HtmlTable
{
const ID = 'tableAllColumns';
const FOOTER_ICON = 'icon-table-more';
const FOOTER_ICON_TITLE = 'General_DisplayTableWithMoreMetrics';
public function beforeRender()
{
$this->config->show_extra_columns = true;
parent::beforeRender();
}
public function beforeGenericFiltersAreAppliedToLoadedDataTable()
{
$this->config->datatable_css_class = 'dataTableVizAllColumns';
$this->dataTable->filter('AddColumnsProcessedMetrics');
$properties = $this->config;
$this->dataTable->filter(function (DataTable $dataTable) use ($properties) {
$columnsToDisplay = array('label', 'nb_visits');
$columns = $dataTable->getColumns();
if (in_array('nb_uniq_visitors', $columns)) {
$columnsToDisplay[] = 'nb_uniq_visitors';
}
if (in_array('nb_users', $columns)) {
$columnsToDisplay[] = 'nb_users';
}
$columnsToDisplay = array_merge(
$columnsToDisplay, array('nb_actions', 'nb_actions_per_visit', 'avg_time_on_site', 'bounce_rate')
);
// only display conversion rate for the plugins that do not provide "per goal" metrics
// otherwise, conversion rate is meaningless as a whole (since we don't process 'cross goals' conversions)
if (!$properties->show_goals) {
$columnsToDisplay[] = 'conversion_rate';
}
$properties->columns_to_display = $columnsToDisplay;
});
}
public function beforeLoadDataTable()
{
unset($this->requestConfig->request_parameters_to_modify['pivotBy']);
unset($this->requestConfig->request_parameters_to_modify['pivotByColumn']);
}
protected function isPivoted()
{
return false; // Pivot not supported
}
}

View File

@ -0,0 +1,132 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
use Piwik\ViewDataTable\Config as VisualizationConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class Config extends VisualizationConfig
{
/**
* If this property is set to true, subtables will be shown as embedded in the original table.
* If false, subtables will be shown as whole tables between rows.
*
* Default value: false
*/
public $show_embedded_subtable = false;
/**
* Controls whether the entire DataTable should be rendered (including subtables) or just one
* specific table in the tree.
*
* Default value: false
*/
public $show_expanded = false;
/**
* When showing an expanded datatable, this property controls whether rows with subtables are
* replaced with their subtables, or if they are shown alongside their subtables.
*
* Default value: false
*/
public $replace_row_with_subtable = false;
/**
* Controls whether any DataTable Row Action icons are shown. If true, no icons are shown.
*
* Default value: false
*/
public $disable_row_actions = false;
/**
* Controls whether the row evolution DataTable Row Action icon is shown or not.
*
* Default value: false
*/
public $disable_row_evolution = false;
/**
* If true, the 'label', 'nb_visits', 'nb_uniq_visitors' (if present), 'nb_actions',
* 'nb_actions_per_visit', 'avg_time_on_site', 'bounce_rate' and 'conversion_rate' (if
* goals view is not allowed) are displayed.
*
* Default value: false
*/
public $show_extra_columns = false;
/**
* If true, conversions for each existing goal will be displayed for the visits in
* each row.
*
* Default value: false
*/
public $show_goals_columns = false;
/**
* If true, subtables will not be loaded when rows are clicked, but only if the
* 'show_goals_columns' property is also true.
*
* Default value: false
*/
public $disable_subtable_when_show_goals = false;
/**
* If true, the summary row will be colored differently than all other DataTable rows.
*
* Default value: false
*/
public $highlight_summary_row = false;
/**
* If true, the totals row will be shown
*
* Default value: false
*/
public $show_totals_row = true;
/**
* A list of columns that support showing the ratio percentage on hover
* @var array
*/
public $report_ratio_columns = array();
public function __construct()
{
parent::__construct();
$this->enable_sort = true;
$this->datatable_js_type = 'DataTable';
$this->addPropertiesThatShouldBeAvailableClientSide(array(
'show_extra_columns',
'show_goals_columns',
'disable_row_evolution',
'disable_row_actions',
'enable_sort',
'keep_summary_row',
'subtable_controller_action',
));
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array(
'show_expanded',
'disable_row_actions',
'disable_row_evolution',
'show_extra_columns',
'show_goals_columns',
'disable_subtable_when_show_goals',
'keep_summary_row',
'highlight_summary_row',
));
}
}

View File

@ -0,0 +1,70 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
use Piwik\Common;
use Piwik\Config as PiwikConfig;
use Piwik\Metrics;
use Piwik\ViewDataTable\RequestConfig as VisualizationRequestConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class RequestConfig extends VisualizationRequestConfig
{
/**
* Controls whether the summary row is displayed on every page of the datatable view or not.
* If false, the summary row will be treated as the last row of the dataset and will only visible
* when viewing the last rows.
*
* Default value: false
*/
public $keep_summary_row = false;
/**
* Controls whether the totals row is displayed on every page of the datatable view or not.
*
* Default value: false
*/
public $keep_totals_row = false;
public function __construct()
{
$this->totals = true;
$this->filter_limit = PiwikConfig::getInstance()->General['datatable_default_limit'];
if (Common::getRequestVar('enable_filter_excludelowpop', false) == '1') {
if (Common::getRequestVar('flat', 0, 'int') === 1) {
$this->filter_excludelowpop = 'nb_visits';
} else {
$this->filter_excludelowpop = Metrics::INDEX_NB_VISITS;
}
$this->filter_excludelowpop_value = false;
}
$this->addPropertiesThatShouldBeAvailableClientSide(array(
'search_recursive',
'filter_limit',
'filter_offset',
'filter_sort_column',
'filter_sort_order',
'keep_summary_row',
'keep_totals_row'
));
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array(
'keep_summary_row',
'keep_totals_row',
));
}
}

View File

@ -0,0 +1,47 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
use Piwik\DataTable;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\View;
/**
* DataTable visualization that displays DataTable data in a JQPlot graph.
* TODO: should merge all this logic w/ jqplotdatagenerator & 'Chart' visualizations.
*
* @property JqplotGraph\Config $config
*/
abstract class JqplotGraph extends Graph
{
const ID = 'jqplot_graph';
const TEMPLATE_FILE = '@CoreVisualizations/_dataTableViz_jqplotGraph.twig';
public static function getDefaultConfig()
{
return new JqplotGraph\Config();
}
public function getGraphData($dataTable, $properties)
{
$dataGenerator = $this->makeDataGenerator($properties);
return $dataGenerator->generate($dataTable);
}
/**
* @param $properties
* @return JqplotDataGenerator
*/
abstract protected function makeDataGenerator($properties);
}
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php';
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php';
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/JqplotGraph/Evolution.php';

View File

@ -0,0 +1,43 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
/**
* Visualization that renders HTML for a Bar graph using jqPlot.
*/
class Bar extends JqplotGraph
{
const ID = 'graphVerticalBar';
const FOOTER_ICON = 'icon-chart-bar';
const FOOTER_ICON_TITLE = 'General_VBarGraph';
public function beforeLoadDataTable()
{
parent::beforeLoadDataTable();
$this->config->datatable_js_type = 'JqplotBarGraphDataTable';
}
public static function getDefaultConfig()
{
$config = new Config();
$config->max_graph_elements = 6;
return $config;
}
protected function makeDataGenerator($properties)
{
return JqplotDataGenerator::factory('bar', $properties);
}
}

View File

@ -0,0 +1,65 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
use Piwik\Plugins\CoreVisualizations\Visualizations\Graph\Config as GraphConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class Config extends GraphConfig
{
/**
* The name of the JavaScript class to use as this graph's external series toggle. The class
* must be a subclass of JQPlotExternalSeriesToggle.
*
* @see self::EXTERNAL_SERIES_TOGGLE_SHOW_ALL
*
* Default value: false
*/
public $external_series_toggle = false;
/**
* Whether the graph should show all loaded series upon initial display.
*
* @see self::EXTERNAL_SERIES_TOGGLE
*
* Default value: false
*/
public $external_series_toggle_show_all = false;
/**
* The number of x-axis ticks for each x-axis label.
*
* Default: 2
*/
public $x_axis_step_size = 2;
public function __construct()
{
parent::__construct();
$this->show_exclude_low_population = false;
$this->show_offset_information = false;
$this->show_pagination_control = false;
$this->show_exclude_low_population = false;
$this->show_search = false;
$this->show_export_as_image_icon = true;
$this->y_axis_unit = '';
$this->addPropertiesThatShouldBeAvailableClientSide(array(
'external_series_toggle',
'external_series_toggle_show_all'
));
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array('x_axis_step_size'));
}
}

View File

@ -0,0 +1,203 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Period\Range;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
use Piwik\Site;
/**
* Visualization that renders HTML for a line graph using jqPlot.
*
* @property Evolution\Config $config
*/
class Evolution extends JqplotGraph
{
const ID = 'graphEvolution';
const SERIES_COLOR_COUNT = 8;
public static function getDefaultConfig()
{
return new Evolution\Config();
}
public function beforeRender()
{
parent::beforeRender();
$this->config->show_flatten_table = false;
$this->config->datatable_js_type = 'JqplotEvolutionGraphDataTable';
}
public function beforeLoadDataTable()
{
$this->calculateEvolutionDateRange();
parent::beforeLoadDataTable();
// period will be overridden when 'range' is requested in the UI
// but the graph will display for each day of the range.
// Default 'range' behavior is to return the 'sum' for the range
if (Common::getRequestVar('period', false) == 'range') {
$this->requestConfig->request_parameters_to_modify['period'] = 'day';
}
$this->config->custom_parameters['columns'] = $this->config->columns_to_display;
}
public function afterAllFiltersAreApplied()
{
parent::afterAllFiltersAreApplied();
if (false === $this->config->x_axis_step_size) {
$rowCount = $this->dataTable->getRowsCount();
$this->config->x_axis_step_size = $this->getDefaultXAxisStepSize($rowCount);
}
}
protected function makeDataGenerator($properties)
{
return JqplotDataGenerator::factory('evolution', $properties);
}
/**
* Based on the period, date and evolution_{$period}_last_n query parameters,
* calculates the date range this evolution chart will display data for.
*/
private function calculateEvolutionDateRange()
{
$period = Common::getRequestVar('period');
$idSite = Common::getRequestVar('idSite');
$timezone = Site::getTimezoneFor($idSite);
$defaultLastN = self::getDefaultLastN($period);
$originalDate = Common::getRequestVar('date', 'last' . $defaultLastN, 'string');
if ('range' != $period) { // show evolution limit if the period is not a range
$this->config->show_limit_control = true;
$this->config->show_periods = true;
// set the evolution_{$period}_last_n query param
if (Range::parseDateRange($originalDate)) {
// if a multiple period
// overwrite last_n param using the date range
$oPeriod = new Range($period, $originalDate, $timezone);
$lastN = count($oPeriod->getSubperiods());
} else {
// if not a multiple period
list($newDate, $lastN) = self::getDateRangeAndLastN($period, $originalDate, $defaultLastN);
$this->requestConfig->request_parameters_to_modify['date'] = $newDate;
$this->config->custom_parameters['dateUsedInGraph'] = $newDate;
}
$lastNParamName = self::getLastNParamName($period);
$this->config->custom_parameters[$lastNParamName] = $lastN;
}
}
/**
* Returns the entire date range and lastN value for the current request, based on
* a period type and end date.
*
* @param string $period The period type, 'day', 'week', 'month' or 'year'
* @param string $endDate The end date.
* @param int|null $defaultLastN The default lastN to use. If null, the result of
* getDefaultLastN is used.
* @return array An array w/ two elements. The first is a whole date range and the second
* is the lastN number used, ie, array('2010-01-01,2012-01-02', 2).
*/
public static function getDateRangeAndLastN($period, $endDate, $defaultLastN = null)
{
if ($defaultLastN === null) {
$defaultLastN = self::getDefaultLastN($period);
}
$lastNParamName = self::getLastNParamName($period);
$lastN = Common::getRequestVar($lastNParamName, $defaultLastN, 'int');
$site = new Site(Common::getRequestVar('idSite'));
$dateRange = Range::getRelativeToEndDate($period, 'last' . $lastN, $endDate, $site);
return array($dateRange, $lastN);
}
/**
* Returns the default last N number of dates to display for a given period.
*
* @param string $period 'day', 'week', 'month' or 'year'
* @return int
*/
public static function getDefaultLastN($period)
{
switch ($period) {
case 'week':
return 26;
case 'month':
return 24;
case 'year':
return 5;
case 'day':
default:
return 30;
}
}
/**
* Returns the query parameter that stores the lastN number of periods to get for
* the evolution graph.
*
* @param string $period The period type, 'day', 'week', 'month' or 'year'.
* @return string
*/
public static function getLastNParamName($period)
{
return "evolution_{$period}_last_n";
}
public function getDefaultXAxisStepSize($countGraphElements)
{
// when the number of elements plotted can be small, make sure the X legend is useful
if ($countGraphElements <= 7) {
return 1;
}
$periodLabel = Common::getRequestVar('period');
switch ($periodLabel) {
case 'day':
case 'range':
$steps = 5;
break;
case 'week':
$steps = 4;
break;
case 'month':
$steps = 5;
break;
case 'year':
$steps = 5;
break;
default:
$steps = 5;
break;
}
$paddedCount = $countGraphElements + 2; // pad count so last label won't be cut off
return ceil($paddedCount / $steps);
}
}

View File

@ -0,0 +1,41 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution;
use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Config as JqplotGraphConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class Config extends JqplotGraphConfig
{
/**
* Whether to show a line graph or a bar graph.
*
* Default value: true
*/
public $show_line_graph = true;
public function __construct()
{
parent::__construct();
$this->show_all_views_icons = false;
$this->show_table = false;
$this->show_table_all_columns = false;
$this->hide_annotations_view = false;
$this->x_axis_step_size = false;
$this->show_line_graph = true;
$this->addPropertiesThatShouldBeAvailableClientSide(array('show_line_graph'));
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array('show_line_graph'));
}
}

View File

@ -0,0 +1,58 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
/**
* Visualization that renders HTML for a Pie graph using jqPlot.
*/
class Pie extends JqplotGraph
{
const ID = 'graphPie';
const FOOTER_ICON = 'icon-chart-pie';
const FOOTER_ICON_TITLE = 'General_Piechart';
public static function getDefaultConfig()
{
$config = new Config();
$config->max_graph_elements = 6;
$config->allow_multi_select_series_picker = false;
return $config;
}
public function beforeRender()
{
parent::beforeRender();
$this->config->show_all_ticks = true;
$this->config->datatable_js_type = 'JqplotPieGraphDataTable';
}
public function afterAllFiltersAreApplied()
{
parent::afterAllFiltersAreApplied();
$metricColumn = reset($this->config->columns_to_display);
if ($metricColumn == 'label') {
$metricColumn = next($this->config->columns_to_display);
}
$this->config->columns_to_display = array($metricColumn ? : 'nb_visits');
}
protected function makeDataGenerator($properties)
{
return JqplotDataGenerator::factory('pie', $properties);
}
}

View File

@ -0,0 +1,128 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
use Exception;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Plugin\ViewDataTable;
/**
* Reads the requested DataTable from the API and prepare data for the Sparkline view.
*
*/
class Sparkline extends ViewDataTable
{
const ID = 'sparkline';
/**
* @see ViewDataTable::main()
* @return mixed
*/
public function render()
{
// If period=range, we force the sparkline to draw daily data points
$period = Common::getRequestVar('period');
if ($period == 'range') {
$_GET['period'] = 'day';
}
$this->loadDataTableFromAPI();
// then revert the hack for potentially subsequent getRequestVar
$_GET['period'] = $period;
$values = $this->getValuesFromDataTable($this->dataTable);
if (empty($values)) {
$values = array_fill(0, 30, 0);
}
$graph = new \Piwik\Visualization\Sparkline();
$graph->setValues($values);
$height = Common::getRequestVar('height', 0, 'int');
if (!empty($height)) {
$graph->setHeight($height);
}
$width = Common::getRequestVar('width', 0, 'int');
if (!empty($width)) {
$graph->setWidth($width);
}
$graph->main();
return $graph->render();
}
/**
* @param DataTable\Map $dataTableMap
* @param string $columnToPlot
*
* @return array
* @throws \Exception
*/
protected function getValuesFromDataTableMap($dataTableMap, $columnToPlot)
{
$dataTableMap->applyQueuedFilters();
$values = array();
foreach ($dataTableMap->getDataTables() as $table) {
if ($table->getRowsCount() > 1) {
throw new Exception("Expecting only one row per DataTable");
}
$value = 0;
$onlyRow = $table->getFirstRow();
if (false !== $onlyRow) {
if (!empty($columnToPlot)) {
$value = $onlyRow->getColumn($columnToPlot);
} // if not specified, we load by default the first column found
// eg. case of getLastDistinctCountriesGraph
else {
$columns = $onlyRow->getColumns();
$value = current($columns);
}
}
$values[] = $value;
}
return $values;
}
protected function getValuesFromDataTable($dataTable)
{
$columns = $this->config->columns_to_display;
$columnToPlot = false;
if (!empty($columns)) {
$columnToPlot = reset($columns);
if ($columnToPlot == 'label') {
$columnToPlot = next($columns);
}
}
// a Set is returned when using the normal code path to request data from Archives, in all core plugins
// however plugins can also return simple datatable, hence why the sparkline can accept both data types
if ($this->dataTable instanceof DataTable\Map) {
$values = $this->getValuesFromDataTableMap($dataTable, $columnToPlot);
} elseif ($this->dataTable instanceof DataTable) {
$values = $this->dataTable->getColumn($columnToPlot);
} else {
$values = false;
}
return $values;
}
}

View File

@ -0,0 +1,164 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Metrics;
use Piwik\Plugin\ViewDataTable;
use Piwik\Url;
use Piwik\View;
/**
* Reads the requested DataTable from the API and prepares data for the Sparklines view. It can display any amount
* of sparklines. Within a reporting page sparklines are shown in 2 columns, in a dashboard or when exported as a widget
* the sparklines are shown in one column.
*
* The sparklines view currently only supports requesting columns from the same API (the API method of the defining
* report) via {Sparklines\Config::addSparklineMetric($columns = array('nb_visits', 'nb_unique_visitors'))}.
*
* Example:
* $view->config->addSparklineMetric('nb_visits'); // if an array of metrics given, they will be displayed comma separated
* $view->config->addTranslation('nb_visits', 'Visits');
* Results in: [sparkline image] X visits
* Data is fetched from the configured $view->requestConfig->apiMethodToRequestDataTable.
*
* In case you want to add any custom sparklines from any other API method you can call
* {@link Sparklines\Config::addSparkline()}.
*
* Example:
* $sparklineUrlParams = array('columns' => array('nb_visits));
* $evolution = array('currentValue' => 5, 'pastValue' => 10, 'tooltip' => 'Foo bar');
* $view->config->addSparkline($sparklineUrlParams, $value = 5, $description = 'Visits', $evolution);
*
* @property Sparklines\Config $config
*/
class Sparklines extends ViewDataTable
{
const ID = 'sparklines';
public static function getDefaultConfig()
{
return new Sparklines\Config();
}
/**
* @see ViewDataTable::main()
* @return mixed
*/
public function render()
{
$view = new View('@CoreVisualizations/_dataTableViz_sparklines.twig');
$columnsList = array();
if ($this->config->hasSparklineMetrics()) {
foreach ($this->config->getSparklineMetrics() as $cols) {
$columns = $cols['columns'];
if (!is_array($columns)) {
$columns = array($columns);
}
$columnsList = array_merge($columns, $columnsList);
}
}
$view->allMetricsDocumentation = Metrics::getDefaultMetricsDocumentation();
$this->requestConfig->request_parameters_to_modify['columns'] = $columnsList;
$this->requestConfig->request_parameters_to_modify['format_metrics'] = '1';
if (!empty($this->requestConfig->apiMethodToRequestDataTable)) {
$this->fetchConfiguredSparklines();
}
$view->sparklines = $this->config->getSortedSparklines();
$view->isWidget = Common::getRequestVar('widget', 0, 'int');
$view->titleAttributes = $this->config->title_attributes;
$view->footerMessage = $this->config->show_footer_message;
$view->areSparklinesLinkable = $this->config->areSparklinesLinkable();
$view->title = '';
if ($this->config->show_title) {
$view->title = $this->config->title;
}
return $view->render();
}
private function fetchConfiguredSparklines()
{
$data = $this->loadDataTableFromAPI();
$this->applyFilters($data);
if (!$this->config->hasSparklineMetrics()) {
foreach ($data->getColumns() as $column) {
$this->config->addSparklineMetric($column);
}
}
$translations = $this->config->translations;
$firstRow = $data->getFirstRow();
foreach ($this->config->getSparklineMetrics() as $sparklineMetric) {
$column = $sparklineMetric['columns'];
$order = $sparklineMetric['order'];
if ($column === 'label') {
continue;
}
if (empty($column)) {
$this->config->addPlaceholder($order);
continue;
}
if (!is_array($column)) {
$column = array($column);
}
$values = array();
$descriptions = array();
foreach ($column as $col) {
$value = $firstRow->getColumn($col);
if ($value === false) {
$value = 0;
}
$values[] = $value;
$descriptions[] = isset($translations[$col]) ? $translations[$col] : $col;
}
$sparklineUrlParams = array(
'columns' => $column,
'module' => $this->requestConfig->getApiModuleToRequest(),
'action' => $this->requestConfig->getApiMethodToRequest()
);
$this->config->addSparkline($sparklineUrlParams, $values, $descriptions, null, $order);
}
}
private function applyFilters(DataTable\DataTableInterface $table)
{
foreach ($this->config->getPriorityFilters() as $filter) {
$table->filter($filter[0], $filter[1]);
}
// queue other filters so they can be applied later if queued filters are disabled
foreach ($this->config->getPresentationFilters() as $filter) {
$table->queueFilter($filter[0], $filter[1]);
}
$table->applyQueuedFilters();
}
}

View File

@ -0,0 +1,399 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.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();
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.
*/
public function addSparklineMetric($metricName, $order = null)
{
$this->sparkline_metrics[] = array(
'columns' => $metricName,
'order' => $order
);
}
/**
* 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)
);
}
/**
* 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.
* @throws \Exception In case an evolution parameter is set but has wrong data structure
*/
public function addSparkline($requestParamsForSparkline, $value, $description, $evolution = null, $order = null)
{
$metrics = array();
if (is_array($value)) {
$values = $value;
} else {
$values = array($value);
}
if (!is_array($description)) {
$description = array($description);
}
if (!empty($requestParamsForSparkline['columns'])
&& is_array($requestParamsForSparkline['columns'])
&& count($requestParamsForSparkline['columns']) === count($values)) {
$columns = array_values($requestParamsForSparkline['columns']);
} elseif (!empty($requestParamsForSparkline['columns'])
&& is_string($requestParamsForSparkline['columns'])
&& count($values) === 1) {
$columns = array($requestParamsForSparkline['columns']);
} else{
$columns = array();
}
if (count($values) === count($description)) {
foreach ($values as $index => $value) {
$metrics[] = array(
'column' => isset($columns[$index]) ? $columns[$index] : '',
'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($metrics)) {
return;
}
$sparkline = array(
'url' => $this->getUrlSparkline($requestParamsForSparkline),
'metrics' => $metrics,
'order' => $this->getSparklineOrder($order)
);
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
);
}
}
$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;
});
return $this->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.");
}
$paramDate = Range::getRelativeToEndDate($period, $range, $endDate, $site);
$params = array_merge($paramsToSet, array('date' => $paramDate));
return $params;
}
}