Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordiosmosis <diosmosis@users.noreply.github.com>2019-09-30 20:19:46 +0300
committerGitHub <noreply@github.com>2019-09-30 20:19:46 +0300
commit3f26e785f015d30d0aeea66aaf7484111b0dbfa9 (patch)
tree3a3d38441103ad8fafd012a027e327faed845817 /plugins/CoreVisualizations
parent98837a7ac01f79f9e713471962699af74a54c6af (diff)
Compare segments and periods (in API and UI) (#14365)
* Allow row metadata to be datatables in API output. * Fill out initial DataComparisonFilter. * fixing some issues * couple more fixes * couple more fixes + initial system test * more fixes * Finish up segment comparison system test and get to pass. * Soft limit for number of segments/periods. * Add supportsComparison method. * Add UX code for adding/removing/seeing data comparisons + code to forward query parameters in AJAX requests + allow broadcast to handle multi-value query param values. * Start showing comparison tables in html tables. * Adding all comparison rows to html table visualization and adding "all visits" segment translation and add currently selected segment to comparisons table. * Show totals ratio for comparison rows. * finished poc html table visualization support for comparison * start working on comparisons support in graph visualizations * Some UX tweaks to htmltable and add comparisons to bar/pie graphs. * Getting comparisons to work in evolution graphs. * Get row evolution to work properly in comparison table. * Get segmented visitor log to work in comparison tables. * Fix regression in comparisons in evolution graph. * Get comparisons to work in actions datatable, fix twig issue that results in 100% cpu usage (when reading dataTable param w/ many rows & comparison tables), get overlay/transitions icons to appear, overlay should work properly. * Get transitions and overlay to work in comparison rows. * Fixing some datatable API output issues, fixing tests, support comparisons in subtables by forcing idSubtables of comparisons to be sent in request (makes UI work, but not pracitcal for API). * Remove typo. * apply original change * Allow All Visits default segment to be compared. * working on disabling currently compared segments. * Get currently compared segments code to work. * starting on refactoring datacomparisonfilter * Most of refactor done. * Get tests to pass and fix a bunch of datatable metadata consistency issues. * Modify evolution graph to modify compare parameters and show some sort of accurate comparison line graphs. * Set xaxis labels correctly in tooltips and make sure series data for comparisons is set correctly. * more fixes to displaying evolution comparisons where compared date ranges vary in length + make sure normal reports w/ no data display the no data message even when comparing * Show period type in comparison card. * Unsanitize compare segments. * Get correct period count. * Couple more fixes to evolution graph series labels, but still wonky. * Include comparison series label in comparison output so evolution graph has an easier time of building series data. * For multi period vs multi period show correct compareDate/comparePeriod for child tables. * Redesign period selector comparison section and get to work. * Allow plugins to disable comparisons for specific pages. * Start supporting comparison in sparkline visualization. * Get sparkline points & lengths to work correctly when comparing. * Fix comparison enabling check. * Pick series and shade colors. * Rewriting comparison card to show individual serieses. * Rewrite comparisons cards to only show segments as cards and individual serieses inside the cards. * Use comparison colors and shades in evolution graph + fix a couple bugs. * Tweak series colors and fix a couple regressions to comparison totals calculation. * Add ratio tooltip suffix explaining comparison percent. * fix typo * Forward comparison params in report export. * tweak series colors again + add tooltip with visitssummary metrics to comparison rows + fix a bug in using array query params in piwik-api + fix bug in formatting of comparison table metrics * Tooltip fixes, start on sparklines supporting comparison, modify comparison filter to only calculate change metrics against periods since they are time related. * Sparkline comparison support. * Tweak line thickness and set metric index properly in jqplot data generator. * In sparklines comparison, show evolution for compared period, rewrite top tooltip to be better, fix tooltip issues when multiple metrics used in evolution graph, and get comparison to previous period to work. * Update submodule * Make things look ok w/ a very long segment name, add numbers to compared datatable row labels, fix pie chart colors + a couple other regressions. * more bug fixes * Fix query param retrieval issue. * Do not throw if no comparison params specified, just do nothing. * try to fix a couple warnings * Another query param get fix. * Do not save comparison parameters. * pass by reference * fix JS error * DO not set compare params if not set in URL for dashboard widgets. * Fix comparison table styling in dashboard. * Expand bar graph if there are too many bars when comparing. * tweak comparison bar graph sizing * make sure flatten works w/ comparison * Apply compute processed metrics to comparison tables. * Hack to get Goals.get to be formatted during comparison. * Fix ordering of yunits in evolution graph. * If rows are selected, incorporate into comparison series names. * Format revenue properly in goals comparison sparklines. * First working attempt at adding Referrers.get method for use w/ Sparklines visualization. * get referrers sparklines to work w/ comparison * Finish using new referrers API method and get referrers sparklines/evolution graph to play well w/ each other in comparison mode. * Simplify table comparison view if only comparing periods, no segments. * Take into account visible rows when calculating series metric index. * Get comparison to work when totals rows are added to tables. * Show series color in evolution graph tooltip. * Fix error when loading row evolution/segmented visitor log for compared ranges. * fix regression in normal subtable loading * Fix row style * Forward comparisonIdSubtables parameter if present so it is used when changing limit/offset * Initialize the row index prefix to the filter offset. * Do not show period header if only segments compared in table. * Add UI tests and fix issues so they pass locally. * quick tweak * Fix PHP error * Updating screenshots * Fixing several bugs and updating expected screenshots. * Fix comparison tests and clear some TODO. * Prefix referrers metrics. * Revert "apply original change" This reverts commit 8f6ceb0430e5c7306a777498199ad7db21fd7175. * Show period label if comparing two periods of same type. * segment sanitization fixes * More segment fixes. * Another fix to the tooltip. * Fix related reports when comparing + make totals tooltip clearer + store segment + pretty title in datatable metadata so it does not have to be looked up every time. * Allow disabling comparisons for individual uses of visualizations. * Remove limit on hover for actions tables + fix subtable expansion for normal actions tables. * Make sure parameters are arrays. * Stricter check for empty parameters. * Allow first compared segment to be "removed". * several more fixes * Fixing table cell alignment and width and everything else that broke while making changes (hopefully). * Several fixes, including xss fixes and test fixes and bug fixes for comparisons. * more table css tweaks * Correct workings of previous period/year comparison + always convert periods to ranges when comparing in evolution graph. * Correct workings of previous period/year comparison + always convert periods to ranges when comparing in evolution graph + more css tweaks. * fix more test regressions * Forgot to add file * fix several TODO as well ass get comparison sparklines to have right colors in widgets. * Use DataTable metadata instead of getting available segments. * When comparing periods that do not uniformly support unique visitors, do not display unique visitors metric. * Small refactor and make sure sparklines shows over period w/o using lastN. * more refactoring and fixes * some more refactoring * Move comparison index math to helper methods. * Use piwikUrl.getSearchParam * Process comparison tables like normal tables in API.getProcessedReport. * remove some code redundancy * use new format date method * Add first working unit test for comparisons service. * Finish writing unit test for comparison service. * refactor comparisons service and fix a couple regressions * Fix more TODO items and refactoring. * Fill out more TODO. * Remove more TODO. * Fixing some tests. * another test fix * FIx some more tests. * More test fixes and regression fixes. * Do not add segments to summary rows in actions reports. * more test fixes * fix more tests * more test fixes * Fixing more tests. * Fixing more tests + debugging failing one. * Fix twig loop issue * Make sure empty compare params are not used in URL. * Remove cached request array. * Support comparison rows in multirow evolution popover and LabelFilter. * Tweak placement of some icons. * Forward current segment in reporting menu links. * Fix for split dimension view. * tweak css * Add more tests. * Add year to xlabels in evolution graph (but not xaxis tick). * applying review feedback * Apply more PR feedback * remove debugging code * tweak event docs * Fix test. * fix some test * fix a test and regression * updating tes files + fixing test * Fix regression * Fix dropdown z-index issue (or workaround really). * Fixing tests again. * Update screenshots * Fix bug and remove some debugging code * Apply review feedback. * Make sure ratio tooltips show in widgetized mode. * Fix some UI tests. * Fix tests * Fix a couple more tests.
Diffstat (limited to 'plugins/CoreVisualizations')
-rw-r--r--plugins/CoreVisualizations/JqplotDataGenerator.php142
-rw-r--r--plugins/CoreVisualizations/JqplotDataGenerator/Chart.php46
-rw-r--r--plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php191
-rw-r--r--plugins/CoreVisualizations/Visualizations/Graph.php8
-rw-r--r--plugins/CoreVisualizations/Visualizations/HtmlTable.php25
-rw-r--r--plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php7
-rw-r--r--plugins/CoreVisualizations/Visualizations/JqplotGraph/Evolution.php41
-rw-r--r--plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php2
-rw-r--r--plugins/CoreVisualizations/Visualizations/Sparkline.php102
-rw-r--r--plugins/CoreVisualizations/Visualizations/Sparklines.php170
-rw-r--r--plugins/CoreVisualizations/Visualizations/Sparklines/Config.php110
-rw-r--r--plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.js4
-rw-r--r--plugins/CoreVisualizations/javascripts/jqplot.js30
-rw-r--r--plugins/CoreVisualizations/javascripts/jqplotBarGraph.js17
-rw-r--r--plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js54
-rw-r--r--plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less14
-rw-r--r--plugins/CoreVisualizations/templates/_dataTableViz_htmlTable.twig25
-rw-r--r--plugins/CoreVisualizations/templates/_dataTableViz_htmlTable_comparisons.twig83
-rw-r--r--plugins/CoreVisualizations/templates/_dataTableViz_htmlTable_ratio.twig26
-rw-r--r--plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig31
-rw-r--r--plugins/CoreVisualizations/templates/macros.twig72
-rw-r--r--plugins/CoreVisualizations/tests/Integration/SparklinesConfigTest.php193
-rw-r--r--plugins/CoreVisualizations/tests/Unit/SparklinesConfigTest.php35
23 files changed, 1233 insertions, 195 deletions
diff --git a/plugins/CoreVisualizations/JqplotDataGenerator.php b/plugins/CoreVisualizations/JqplotDataGenerator.php
index d725d22b04..e120764005 100644
--- a/plugins/CoreVisualizations/JqplotDataGenerator.php
+++ b/plugins/CoreVisualizations/JqplotDataGenerator.php
@@ -10,11 +10,16 @@
namespace Piwik\Plugins\CoreVisualizations;
use Exception;
+use Piwik\API\Request;
use Piwik\Common;
use Piwik\DataTable;
+use Piwik\DataTable\Row;
use Piwik\Metrics;
+use Piwik\Period\Factory;
use Piwik\Piwik;
+use Piwik\Plugins\API\Filter\DataComparisonFilter;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator\Chart;
+use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php';
@@ -26,7 +31,7 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/JqplotDataGenerat
class JqplotDataGenerator
{
/**
- * View properties. @see Piwik\ViewDataTable for more info.
+ * View properties. @see \Piwik\ViewDataTable for more info.
*
* @var array
*/
@@ -34,6 +39,15 @@ class JqplotDataGenerator
protected $graphType;
+ protected $isComparing;
+
+ private $availableSegments;
+
+ /**
+ * @var JqplotGraph
+ */
+ protected $graph;
+
/**
* Creates a new JqplotDataGenerator instance for a graph type and view properties.
*
@@ -42,14 +56,14 @@ class JqplotDataGenerator
* @throws \Exception
* @return JqplotDataGenerator
*/
- public static function factory($type, $properties)
+ public static function factory($type, $properties, JqplotGraph $graph)
{
switch ($type) {
case 'evolution':
- return new JqplotDataGenerator\Evolution($properties, $type);
+ return new JqplotDataGenerator\Evolution($properties, $type, $graph);
case 'pie':
case 'bar':
- return new JqplotDataGenerator($properties, $type);
+ return new JqplotDataGenerator($properties, $type, $graph);
default:
throw new Exception("Unknown JqplotDataGenerator type '$type'.");
}
@@ -63,10 +77,14 @@ class JqplotDataGenerator
*
* @internal param \Piwik\Plugin\ViewDataTable $visualization
*/
- public function __construct($properties, $graphType)
+ public function __construct($properties, $graphType, JqplotGraph $graph)
{
$this->properties = $properties;
$this->graphType = $graphType;
+ $this->isComparing = $graph->isComparing();
+ $this->graph = $graph;
+
+ $this->availableSegments = Request::processRequest('SegmentEditor.getAll', $override = [], $default = []);
}
/**
@@ -80,12 +98,6 @@ class JqplotDataGenerator
$visualization = new Chart();
if ($dataTable->getRowsCount() > 0) {
- // if addTotalRow was called in GenerateGraphHTML, add a row containing totals of
- // different metrics
- if ($this->properties['add_total_row']) {
- $dataTable->queueFilter('AddSummaryRow', Piwik::translate('General_Total'));
- }
-
$dataTable->applyQueuedFilters();
$this->initChartObjectData($dataTable, $visualization);
}
@@ -101,47 +113,123 @@ class JqplotDataGenerator
{
$xLabels = $dataTable->getColumn('label');
- $columnNames = $this->properties['columns_to_display'];
- if (($labelColumnIndex = array_search('label', $columnNames)) !== false) {
- unset($columnNames[$labelColumnIndex]);
+ $columnsToDisplay = array_values($this->properties['columns_to_display']);
+ if (($labelColumnIndex = array_search('label', $columnsToDisplay)) !== false) {
+ unset($columnsToDisplay[$labelColumnIndex]);
}
- $columnNameToTranslation = $columnNameToValue = array();
- foreach ($columnNames as $columnName) {
- $columnNameToTranslation[$columnName] = @$this->properties['translations'][$columnName];
-
- $columnNameToValue[$columnName] = $dataTable->getColumn($columnName);
+ $seriesMetadata = null;
+ if ($this->isComparing) {
+ list($yLabels, $serieses, $seriesMetadata) = $this->getComparisonTableSerieses($dataTable, $columnsToDisplay);
+ } else {
+ list($yLabels, $serieses) = $this->getMainTableSerieses($dataTable, $columnsToDisplay);
}
$visualization->dataTable = $dataTable;
$visualization->properties = $this->properties;
$visualization->setAxisXLabels($xLabels);
- $visualization->setAxisYValues($columnNameToValue);
- $visualization->setAxisYLabels($columnNameToTranslation);
+ $visualization->setAxisYValues($serieses, $seriesMetadata);
+ $visualization->setAxisYLabels($yLabels);
- $units = $this->getUnitsForColumnsToDisplay();
+ $units = $this->getUnitsForSerieses($yLabels);
$visualization->setAxisYUnits($units);
}
- protected function getUnitsForColumnsToDisplay()
+ private function getMainTableSerieses(DataTable $dataTable, $columnNames)
+ {
+ $columnNameToTranslation = [];
+
+ foreach ($columnNames as $columnName) {
+ $columnNameToTranslation[$columnName] = @$this->properties['translations'][$columnName];
+ }
+
+ $columnNameToValue = array();
+ foreach ($columnNames as $columnName) {
+ $columnNameToValue[$columnName] = $dataTable->getColumn($columnName);
+ }
+
+ return [$columnNameToTranslation, $columnNameToValue];
+ }
+
+ private function getComparisonTableSerieses(DataTable $dataTable, $columnsToDisplay)
+ {
+ $seriesLabels = [];
+ $serieses = [];
+ $seriesMetadata = [];
+
+ $seriesIndices = [];
+
+ foreach ($dataTable->getRows() as $row) {
+ /** @var DataTable $comparisonTable */
+ $comparisonTable = $row->getComparisons();
+ if (empty($comparisonTable)) {
+ continue;
+ }
+
+ foreach ($comparisonTable->getRows() as $index => $compareRow) {
+ foreach ($columnsToDisplay as $columnIndex => $columnName) {
+ $seriesId = $columnName . '|' . $index;
+
+ if (!isset($seriesIndices[$seriesId])) {
+ $seriesIndices[$seriesId] = count($seriesIndices);
+ }
+
+ $seriesLabel = $this->getComparisonSeriesLabel($compareRow, $columnName);
+ $seriesLabels[$seriesId] = $seriesLabel;
+ $serieses[$seriesId][] = $compareRow->getColumn($columnName);
+
+ $seriesMetadata[$seriesId] = [
+ 'seriesIndex' => $seriesIndices[$seriesId],
+ 'metricIndex' => $columnIndex,
+ ];
+ }
+ }
+ }
+
+ return [$seriesLabels, $serieses, $seriesMetadata];
+ }
+
+ protected function getComparisonSeriesLabel(Row $compareRow, $columnName, $rowLabel = false)
+ {
+ return $this->getComparisonSeriesLabelFromCompareSeries($compareRow->getMetadata('compareSeriesPretty'), $columnName, $rowLabel);
+ }
+
+ protected function getComparisonSeriesLabelFromCompareSeries($compareSeriesPretty, $columnName, $rowLabel = false)
+ {
+ $columnTranslation = @$this->properties['translations'][$columnName];
+
+ if (empty($rowLabel)) {
+ $label = $columnTranslation;
+ } else {
+ $label = "$rowLabel ($columnTranslation)";
+ }
+
+ $label .= ' ' . $compareSeriesPretty;
+ return $label;
+ }
+
+ protected function getUnitsForSerieses($yLabels)
{
// derive units from column names
- $units = $this->deriveUnitsFromRequestedColumnNames();
+ $units = $this->deriveUnitsFromRequestedColumnNames($yLabels);
if (!empty($this->properties['y_axis_unit'])) {
$units = array_fill(0, count($units), $this->properties['y_axis_unit']);
}
return $units;
}
- private function deriveUnitsFromRequestedColumnNames()
+ private function deriveUnitsFromRequestedColumnNames($yLabels)
{
$idSite = Common::getRequestVar('idSite', null, 'int');
$units = array();
- foreach ($this->properties['columns_to_display'] as $columnName) {
+ foreach ($yLabels as $seriesId => $ignore) {
+ $parts = explode('|', $seriesId, 2);
+ $columnName = $parts[0];
+
$derivedUnit = Metrics::getUnit($columnName, $idSite);
- $units[$columnName] = empty($derivedUnit) ? false : $derivedUnit;
+ $units[$seriesId] = empty($derivedUnit) ? false : $derivedUnit;
}
return $units;
}
diff --git a/plugins/CoreVisualizations/JqplotDataGenerator/Chart.php b/plugins/CoreVisualizations/JqplotDataGenerator/Chart.php
index 44be4af356..2ac4b6f8d7 100644
--- a/plugins/CoreVisualizations/JqplotDataGenerator/Chart.php
+++ b/plugins/CoreVisualizations/JqplotDataGenerator/Chart.php
@@ -25,14 +25,16 @@ class Chart
// temporary
public $properties;
- public function setAxisXLabels($xLabels)
+ public function setAxisXLabels($xLabels, $xTicks = null, $index = 0)
{
+ $axisName = $this->getXAxis($index);
+
$xSteps = $this->properties['x_axis_step_size'];
$showAllTicks = $this->properties['show_all_ticks'];
- $this->axes['xaxis']['labels'] = array_values($xLabels);
+ $this->axes[$axisName]['labels'] = array_values($xLabels);
- $ticks = array_values($xLabels);
+ $ticks = array_values($xTicks ?: $xLabels);
if (!$showAllTicks) {
// unset labels so there are $xSteps number of blank ticks between labels
@@ -42,7 +44,7 @@ class Chart
}
}
}
- $this->axes['xaxis']['ticks'] = $ticks;
+ $this->axes[$axisName]['ticks'] = $ticks;
}
public function setAxisXOnClick(&$onClick)
@@ -50,14 +52,20 @@ class Chart
$this->axes['xaxis']['onclick'] = & $onClick;
}
- public function setAxisYValues(&$values)
+ public function setAxisYValues(&$values, $seriesMetadata = null)
{
foreach ($values as $label => &$data) {
- $this->series[] = array(
+ $seriesInfo = array(
'label' => $label,
- 'internalLabel' => $label
+ 'internalLabel' => $label,
);
+ if (isset($seriesMetadata[$label])) {
+ $seriesInfo = array_merge($seriesInfo, $seriesMetadata[$label]);
+ }
+
+ $this->series[] = $seriesInfo;
+
array_walk($data, function (&$v) {
$v = (float) Common::forceDotAsSeparatorForDecimalPoint($v);
});
@@ -116,4 +124,28 @@ class Chart
return $data;
}
+
+ public function setAxisXLabelsMultiple($xLabels, $seriesToXAxis, $ticks = null)
+ {
+ foreach ($xLabels as $index => $labels) {
+ $this->setAxisXLabels($labels, $ticks === null ? null : $ticks[$index], $index);
+ }
+
+ foreach ($seriesToXAxis as $seriesIndex => $xAxisIndex) {
+ $axisName = $this->getXAxis($xAxisIndex);
+
+ // don't actually set xaxis otherwise jqplot will show too many axes. however, we need the xaxis labels, so we add them
+ // to the jqplot config
+ $this->series[$seriesIndex]['_xaxis'] = $axisName;
+ }
+ }
+
+ private function getXAxis($index)
+ {
+ $axisName = 'xaxis';
+ if ($index != 0) {
+ $axisName = 'x' . ($index + 1) . 'axis';
+ }
+ return $axisName;
+ }
}
diff --git a/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php b/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php
index cae2577965..814903265b 100644
--- a/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php
+++ b/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php
@@ -11,6 +11,13 @@ namespace Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Archive\DataTableFactory;
use Piwik\Common;
use Piwik\DataTable;
+use Piwik\DataTable\DataTableInterface;
+use Piwik\DataTable\Row;
+use Piwik\Date;
+use Piwik\Metrics;
+use Piwik\Period;
+use Piwik\Period\Factory;
+use Piwik\Plugins\API\Filter\DataComparisonFilter;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Url;
@@ -19,6 +26,18 @@ use Piwik\Url;
*/
class Evolution extends JqplotDataGenerator
{
+ protected function getUnitsForColumnsToDisplay()
+ {
+ $idSite = Common::getRequestVar('idSite', null, 'int');
+
+ $units = [];
+ foreach ($this->properties['columns_to_display'] as $columnName) {
+ $derivedUnit = Metrics::getUnit($columnName, $idSite);
+ $units[$columnName] = empty($derivedUnit) ? false : $derivedUnit;
+ }
+ return $units;
+ }
+
/**
* @param DataTable|DataTable\Map $dataTable
* @param Chart $visualization
@@ -33,11 +52,16 @@ class Evolution extends JqplotDataGenerator
return;
}
- // the X label is extracted from the 'period' object in the table's metadata
- $xLabels = array();
- foreach ($dataTable->getDataTables() as $metadataDataTable) {
- $xLabels[] = $metadataDataTable->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getLocalizedShortString(); // eg. "Aug 2009"
- }
+ $dataTables = $dataTable->getDataTables();
+
+ // determine x labels based on both the displayed date range and the compared periods
+ /** @var Period[][] $xLabels */
+ $xLabels = [
+ [], // placeholder for first series
+ ];
+
+ $this->addComparisonXLabels($xLabels, reset($dataTables));
+ $this->addSelectedSeriesXLabels($xLabels, $dataTables);
$units = $this->getUnitsForColumnsToDisplay();
@@ -47,27 +71,37 @@ class Evolution extends JqplotDataGenerator
? : array(false) // make sure that a series is plotted even if there is no data
;
+ $columnsToDisplay = array_values($this->properties['columns_to_display']);
+
+ list($seriesMetadata, $seriesUnits, $seriesLabels, $seriesToXAxis) =
+ $this->getSeriesMetadata($rowsToDisplay, $columnsToDisplay, $units, $dataTables);
+
// collect series data to show. each row-to-display/column-to-display permutation creates a series.
$allSeriesData = array();
- $seriesUnits = array();
foreach ($rowsToDisplay as $rowLabel) {
- foreach ($this->properties['columns_to_display'] as $columnName) {
- $seriesLabel = $this->getSeriesLabel($rowLabel, $columnName);
- $seriesData = $this->getSeriesData($rowLabel, $columnName, $dataTable);
-
- $allSeriesData[$seriesLabel] = $seriesData;
- $seriesUnits[$seriesLabel] = $units[$columnName];
+ foreach ($columnsToDisplay as $columnName) {
+ if (!$this->isComparing) {
+ $this->setNonComparisonSeriesData($allSeriesData, $rowLabel, $columnName, $dataTable);
+ } else {
+ $this->setComparisonSeriesData($allSeriesData, $seriesLabels, $rowLabel, $columnName, $dataTable);
+ }
}
}
$visualization->dataTable = $dataTable;
$visualization->properties = $this->properties;
- $visualization->setAxisXLabels($xLabels);
- $visualization->setAxisYValues($allSeriesData);
+ $visualization->setAxisYValues($allSeriesData, $seriesMetadata);
$visualization->setAxisYUnits($seriesUnits);
- $dataTables = $dataTable->getDataTables();
+ $xLabelStrs = [];
+ $xAxisTicks = [];
+ foreach ($xLabels as $index => $seriesXLabels) {
+ $xLabelStrs[$index] = array_map(function (Period $p) { return $p->getLocalizedLongString(); }, $seriesXLabels);
+ $xAxisTicks[$index] = array_map(function (Period $p) { return $p->getLocalizedShortString(); }, $seriesXLabels);
+ }
+
+ $visualization->setAxisXLabelsMultiple($xLabelStrs, $seriesToXAxis, $xAxisTicks);
if ($this->isLinkEnabled()) {
$idSite = Common::getRequestVar('idSite', null, 'int');
@@ -144,4 +178,131 @@ class Evolution extends JqplotDataGenerator
}
return $linkEnabled;
}
+
+ /**
+ * Each period comparison shows data over different data points than the main series (eg, 2014-02-03,1014-02-06 compared w/ 2015-03-04,2015-03-15).
+ * Though we only display the selected period's x labels, we need to both have the labels for all these data points for tooltips and to stretch
+ * out the selected period x axis, in case it is shorter than one of the compared periods (as in the example above).
+ */
+ private function addComparisonXLabels(array &$xLabels, DataTable $table)
+ {
+ $comparePeriods = $table->getMetadata('comparePeriods') ?: [];
+ $compareDates = $table->getMetadata('compareDates') ?: [];
+
+ // get rid of selected period
+ array_shift($comparePeriods);
+ array_shift($compareDates);
+
+ foreach (array_values($comparePeriods) as $index => $period) {
+ $date = $compareDates[$index];
+
+ $range = Factory::build($period, $date);
+ foreach ($range->getSubperiods() as $subperiod) {
+ $xLabels[$index + 1][] = $subperiod;
+ }
+ }
+ }
+
+ /**
+ * @param array $xLabels
+ * @param DataTable[] $dataTables
+ * @throws \Exception
+ */
+ private function addSelectedSeriesXLabels(array &$xLabels, array $dataTables)
+ {
+ $xTicksCount = count($dataTables);
+ foreach ($xLabels as $labelSeries) {
+ $xTicksCount = max(count($labelSeries), $xTicksCount);
+ }
+
+ /** @var Date $startDate */
+ $startDate = reset($dataTables)->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getDateStart();
+ $periodType = reset($dataTables)->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getLabel();
+
+ for ($i = 0; $i < $xTicksCount; ++$i) {
+ $period = Factory::build($periodType, $startDate->addPeriod($i, $periodType));
+ $xLabels[0][] = $period;
+ }
+ }
+
+ private function setNonComparisonSeriesData(array &$allSeriesData, $rowLabel, $columnName, DataTable\Map $dataTable)
+ {
+ $seriesLabel = $this->getSeriesLabel($rowLabel, $columnName);
+
+ $seriesData = $this->getSeriesData($rowLabel, $columnName, $dataTable);
+ $allSeriesData[$seriesLabel] = $seriesData;
+ }
+
+ private function setComparisonSeriesData(array &$allSeriesData, array $seriesLabels, $rowLabel, $columnName, DataTable\Map $dataTable)
+ {
+ foreach ($dataTable->getDataTables() as $label => $childTable) {
+ // get the row for this label (use the first if $rowLabel is false)
+ if ($rowLabel === false) {
+ $row = $childTable->getFirstRow();
+ } else {
+ $row = $childTable->getRowFromLabel($rowLabel);
+ }
+
+ if (empty($row)
+ || empty($row->getComparisons())
+ ) {
+ foreach ($seriesLabels as $seriesIndex => $seriesLabelPrefix) {
+ $wholeSeriesLabel = $this->getComparisonSeriesLabelFromCompareSeries($seriesLabelPrefix, $columnName, $rowLabel);
+ $allSeriesData[$wholeSeriesLabel][] = 0;
+ }
+
+ continue;
+ }
+
+ /** @var DataTable $comparisonTable */
+ $comparisonTable = $row->getComparisons();
+ foreach ($comparisonTable->getRows() as $compareRow) {
+ $seriesLabel = $this->getComparisonSeriesLabel($compareRow, $columnName, $rowLabel);
+ $allSeriesData[$seriesLabel][] = $compareRow->getColumn($columnName);
+ }
+
+ $totalsRow = $comparisonTable->getTotalsRow();
+ if ($totalsRow) {
+ $seriesLabel = $this->getComparisonSeriesLabel($totalsRow, $columnName, $rowLabel);
+ $allSeriesData[$seriesLabel][] = $totalsRow->getColumn($columnName);
+ }
+ }
+ }
+
+ private function getSeriesMetadata(array $rowsToDisplay, array $columnsToDisplay, array $units, array $dataTables)
+ {
+ $seriesMetadata = null; // maps series labels to any metadata of the series
+ $seriesUnits = array(); // maps series labels to unit labels
+ $seriesToXAxis = []; // maps series index to x-axis index (groups of metrics for a single comparison will use the same x-axis)
+
+ $table = reset($dataTables);
+ $seriesLabels = $table->getMetadata('comparisonSeries') ?: [];
+ foreach ($rowsToDisplay as $rowIndex => $rowLabel) {
+ foreach ($columnsToDisplay as $columnIndex => $columnName) {
+ if ($this->isComparing) {
+ foreach ($seriesLabels as $seriesIndex => $seriesLabel) {
+ $wholeSeriesLabel = $this->getComparisonSeriesLabelFromCompareSeries($seriesLabel, $columnName, $rowLabel);
+
+ $allSeriesData[$wholeSeriesLabel] = [];
+
+ $metricIndex = $rowIndex * count($columnsToDisplay) + $columnIndex;
+ $seriesMetadata[$wholeSeriesLabel] = [
+ 'metricIndex' => $metricIndex,
+ 'seriesIndex' => $seriesIndex,
+ ];
+
+ $seriesUnits[$wholeSeriesLabel] = $units[$columnName];
+
+ list($periodIndex, $segmentIndex) = DataComparisonFilter::getIndividualComparisonRowIndices($table, $seriesIndex);
+ $seriesToXAxis[] = $periodIndex;
+ }
+ } else {
+ $seriesLabel = $this->getSeriesLabel($rowLabel, $columnName);
+ $seriesUnits[$seriesLabel] = $units[$columnName];
+ }
+ }
+ }
+
+ return [$seriesMetadata, $seriesUnits, $seriesLabels, $seriesToXAxis];
+ }
}
diff --git a/plugins/CoreVisualizations/Visualizations/Graph.php b/plugins/CoreVisualizations/Visualizations/Graph.php
index f7767d9527..bc37ea8747 100644
--- a/plugins/CoreVisualizations/Visualizations/Graph.php
+++ b/plugins/CoreVisualizations/Visualizations/Graph.php
@@ -63,6 +63,14 @@ abstract class Graph extends Visualization
$this->requestConfig->request_parameters_to_modify['format_metrics'] = 1;
+ // if addTotalRow was called in GenerateGraphHTML, add a row containing totals of
+ // different metrics
+ if ($this->config->add_total_row) {
+ $this->requestConfig->request_parameters_to_modify['totals'] = 1;
+ $this->requestConfig->request_parameters_to_modify['keep_totals_row'] = 1;
+ $this->requestConfig->request_parameters_to_modify['keep_totals_row_label'] = Piwik::translate('General_Total');
+ }
+
$this->metricsFormatter = new Numeric();
}
diff --git a/plugins/CoreVisualizations/Visualizations/HtmlTable.php b/plugins/CoreVisualizations/Visualizations/HtmlTable.php
index 4bf717fd1b..a07cc6af2b 100644
--- a/plugins/CoreVisualizations/Visualizations/HtmlTable.php
+++ b/plugins/CoreVisualizations/Visualizations/HtmlTable.php
@@ -42,6 +42,15 @@ class HtmlTable extends Visualization
public function beforeLoadDataTable()
{
$this->checkRequestIsNotForMultiplePeriods();
+
+ if ($this->isComparing()) {
+ // forward the comparisonIdSubtables var if present so it will be used when next/prev links are clicked
+ $comparisonIdSubtables = Common::getRequestVar('comparisonIdSubtables', false, 'string');
+ if (!empty($comparisonIdSubtables)) {
+ $comparisonIdSubtables = Common::unsanitizeInputValue($comparisonIdSubtables);
+ $this->config->custom_parameters['comparisonIdSubtables'] = $comparisonIdSubtables;
+ }
+ }
}
public function beforeRender()
@@ -96,6 +105,12 @@ class HtmlTable extends Visualization
$this->config->columns_to_display = $this->dataTable->getColumns();
}
+ if ($this->isComparing()
+ && !empty($this->dataTable)
+ ) {
+ $this->assignTemplateVar('comparisonTotals', $this->dataTable->getMetadata('comparisonTotals'));
+ }
+
// Note: This needs to be done right before rendering, as otherwise some plugins might change the columns to display again
if ($this->isFlattened()) {
$dimensions = $this->dataTable->getMetadata('dimensions');
@@ -144,6 +159,11 @@ class HtmlTable extends Visualization
});
}
}
+
+ $this->assignTemplateVar('segmentTitlePretty', $this->dataTable->getMetadata('segmentPretty'));
+
+ $period = $this->dataTable->getMetadata('period');
+ $this->assignTemplateVar('periodTitlePretty', $period ? $period->getLocalizedShortString() : '');
}
public function beforeGenericFiltersAreAppliedToLoadedDataTable()
@@ -212,6 +232,11 @@ class HtmlTable extends Visualization
return null;
}
+ public function supportsComparison()
+ {
+ return true;
+ }
+
protected function isFlattened()
{
return $this->requestConfig->flat || Common::getRequestVar('flat', '');
diff --git a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php
index 579016f122..0da05ea68e 100644
--- a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php
+++ b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php
@@ -50,6 +50,11 @@ class Bar extends JqplotGraph
protected function makeDataGenerator($properties)
{
- return JqplotDataGenerator::factory('bar', $properties);
+ return JqplotDataGenerator::factory('bar', $properties, $this);
+ }
+
+ public function supportsComparison()
+ {
+ return true;
}
}
diff --git a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Evolution.php b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Evolution.php
index 5de7fc5114..337bb233b2 100644
--- a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Evolution.php
+++ b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Evolution.php
@@ -9,8 +9,11 @@
namespace Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
+use Piwik\API\Request as ApiRequest;
use Piwik\Common;
use Piwik\DataTable;
+use Piwik\Period;
+use Piwik\Period\Factory;
use Piwik\Period\Range;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
@@ -43,7 +46,9 @@ class Evolution extends JqplotGraph
public function beforeLoadDataTable()
{
- $this->calculateEvolutionDateRange();
+ if (!$this->isComparing()) {
+ $this->calculateEvolutionDateRange();
+ }
parent::beforeLoadDataTable();
@@ -55,6 +60,33 @@ class Evolution extends JqplotGraph
}
$this->config->custom_parameters['columns'] = $this->config->columns_to_display;
+
+ if ($this->isComparing()) {
+ $requestArray = $this->request->getRequestArray();
+ $requestArray = ApiRequest::getRequestArrayFromString($requestArray);
+
+ $requestingPeriod = Factory::build($requestArray['period'], $requestArray['date']);
+
+ $this->requestConfig->request_parameters_to_modify['period'] = 'day';
+ $this->requestConfig->request_parameters_to_modify['date'] = $requestingPeriod->getDateStart()->toString() . ',' . $requestingPeriod->getDateEnd()->toString();
+
+ if (!empty($requestArray['comparePeriods'])) {
+ foreach ($requestArray['comparePeriods'] as $index => $comparePeriod) {
+ $compareDate = $requestArray['compareDates'][$index];
+ if (Period::isMultiplePeriod($compareDate, $comparePeriod)) {
+ continue;
+ }
+
+ $comparePeriodObj = Factory::build($comparePeriod, $compareDate);
+
+ $requestArray['comparePeriods'][$index] = 'day';
+ $requestArray['compareDates'][$index] = $comparePeriodObj->getRangeString();
+ }
+
+ $this->requestConfig->request_parameters_to_modify['compareDates'] = $requestArray['compareDates'];
+ $this->requestConfig->request_parameters_to_modify['comparePeriods'] = $requestArray['comparePeriods'];
+ }
+ }
}
public function afterAllFiltersAreApplied()
@@ -70,7 +102,7 @@ class Evolution extends JqplotGraph
protected function makeDataGenerator($properties)
{
- return JqplotDataGenerator::factory('evolution', $properties);
+ return JqplotDataGenerator::factory('evolution', $properties, $this);
}
/**
@@ -199,4 +231,9 @@ class Evolution extends JqplotGraph
return ceil($paddedCount / $steps);
}
+
+ public function supportsComparison()
+ {
+ return true;
+ }
}
diff --git a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php
index 3fafdd5e7b..045047049f 100644
--- a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php
+++ b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php
@@ -55,6 +55,6 @@ class Pie extends JqplotGraph
protected function makeDataGenerator($properties)
{
- return JqplotDataGenerator::factory('pie', $properties);
+ return JqplotDataGenerator::factory('pie', $properties, $this);
}
}
diff --git a/plugins/CoreVisualizations/Visualizations/Sparkline.php b/plugins/CoreVisualizations/Visualizations/Sparkline.php
index a2bdb09d2c..b59a89ba5d 100644
--- a/plugins/CoreVisualizations/Visualizations/Sparkline.php
+++ b/plugins/CoreVisualizations/Visualizations/Sparkline.php
@@ -11,7 +11,9 @@ namespace Piwik\Plugins\CoreVisualizations\Visualizations;
use Exception;
use Piwik\Common;
use Piwik\DataTable;
+use Piwik\Period;
use Piwik\Plugin\ViewDataTable;
+use Piwik\Site;
/**
* Reads the requested DataTable from the API and prepare data for the Sparkline view.
@@ -21,6 +23,11 @@ class Sparkline extends ViewDataTable
{
const ID = 'sparkline';
+ public function supportsComparison()
+ {
+ return true;
+ }
+
/**
* @see ViewDataTable::main()
* @return mixed
@@ -29,22 +36,61 @@ class Sparkline extends ViewDataTable
{
// If period=range, we force the sparkline to draw daily data points
$period = Common::getRequestVar('period');
- if ($period == 'range') {
+ $date = Common::getRequestVar('date');
+
+ if ($period == 'range'
+ || $this->isComparing()
+ ) {
+ $periodObj = Period\Factory::build($period, $date);
$_GET['period'] = 'day';
+ $_GET['date'] = $periodObj->getRangeString();
+ }
+
+ if ($this->isComparing()) {
+ $this->transformSingleComparisonPeriods();
}
$this->loadDataTableFromAPI();
// then revert the hack for potentially subsequent getRequestVar
$_GET['period'] = $period;
+ $_GET['date'] = $date;
- $values = $this->getValuesFromDataTable($this->dataTable);
- if (empty($values)) {
- $values = array_fill(0, 30, 0);
- }
+ $columnToPlot = $this->getColumnToPlot();
$graph = new \Piwik\Visualization\Sparkline();
- $graph->setValues($values);
+
+ if ($this->isComparing()) {
+ $otherSeries = [];
+
+ $comparisonSeries = $this->getComparisonSeries($this->dataTable);
+ foreach ($comparisonSeries as $seriesName) {
+ $otherSeries[$seriesName] = [];
+ }
+
+ $this->dataTable->filter(function (DataTable $table) use ($comparisonSeries, &$otherSeries, $columnToPlot) {
+ foreach ($table->getRows() as $row) {
+ $comparisons = $row->getComparisons();
+ if (empty($comparisons)) {
+ continue;
+ }
+
+ foreach ($comparisons->getRows() as $comparisonRow) {
+ $compareSeriesPretty = $comparisonRow->getMetadata('compareSeriesPretty');
+ $otherSeries[$compareSeriesPretty][] = $comparisonRow->getColumn($columnToPlot);
+ }
+ }
+ });
+
+ foreach ($otherSeries as $seriesValues) {
+ $seriesValues = $this->ensureValuesEvenIfEmpty($seriesValues);
+ $graph->addSeries($seriesValues);
+ }
+ } else {
+ $values = $this->getValuesFromDataTable($this->dataTable, $columnToPlot);
+ $values = $this->ensureValuesEvenIfEmpty($values);
+ $graph->addSeries($values);
+ }
$height = Common::getRequestVar('height', 0, 'int');
if (!empty($height)) {
@@ -100,7 +146,7 @@ class Sparkline extends ViewDataTable
return $values;
}
- protected function getValuesFromDataTable($dataTable)
+ private function getColumnToPlot()
{
$columns = $this->config->columns_to_display;
@@ -113,6 +159,11 @@ class Sparkline extends ViewDataTable
}
}
+ return $columnToPlot;
+ }
+
+ protected function getValuesFromDataTable($dataTable, $columnToPlot)
+ {
// 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) {
@@ -125,4 +176,41 @@ class Sparkline extends ViewDataTable
return $values;
}
+
+ private function ensureValuesEvenIfEmpty(array $values)
+ {
+ if (empty($values)) {
+ return array_fill(0, 30, 0);
+ }
+ return $values;
+ }
+
+ private function getComparisonSeries(DataTable\DataTableInterface $dataTable)
+ {
+ if ($dataTable instanceof DataTable\Map) {
+ return reset($dataTable->getDataTables())->getMetadata('comparisonSeries') ?: [];
+ } else {
+ return $dataTable->getMetadata('comparisonSeries') ?: [];
+ }
+ }
+
+ private function transformSingleComparisonPeriods()
+ {
+ $comparePeriods = Common::getRequestVar('comparePeriods', $default = [], $type = 'array');
+ $compareDates = Common::getRequestVar('compareDates', $default = [], $type = 'array');
+
+ foreach ($comparePeriods as $index => $comparePeriod) {
+ $compareDate = $compareDates[$index];
+ if (Period::isMultiplePeriod($compareDate, $comparePeriod)) {
+ continue;
+ }
+
+ $periodObj = Period\Factory::build($comparePeriod, $compareDate);
+ $comparePeriods[$index] = 'day';
+ $compareDates[$index] = $periodObj->getRangeString();
+ }
+
+ $this->requestConfig->request_parameters_to_modify['comparePeriods'] = $comparePeriods;
+ $this->requestConfig->request_parameters_to_modify['compareDates'] = $compareDates;
+ }
}
diff --git a/plugins/CoreVisualizations/Visualizations/Sparklines.php b/plugins/CoreVisualizations/Visualizations/Sparklines.php
index 2d11723bef..c3d8556e00 100644
--- a/plugins/CoreVisualizations/Visualizations/Sparklines.php
+++ b/plugins/CoreVisualizations/Visualizations/Sparklines.php
@@ -8,10 +8,14 @@
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
+use Piwik\API\Request;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Metrics;
+use Piwik\Period\Factory;
use Piwik\Plugin\ViewDataTable;
+use Piwik\Plugins\API\Filter\DataComparisonFilter;
+use Piwik\SettingsPiwik;
use Piwik\Url;
use Piwik\View;
@@ -48,6 +52,11 @@ class Sparklines extends ViewDataTable
return new Sparklines\Config();
}
+ public function supportsComparison()
+ {
+ return true;
+ }
+
/**
* @see ViewDataTable::main()
* @return mixed
@@ -82,6 +91,7 @@ class Sparklines extends ViewDataTable
$view->titleAttributes = $this->config->title_attributes;
$view->footerMessage = $this->config->show_footer_message;
$view->areSparklinesLinkable = $this->config->areSparklinesLinkable();
+ $view->isComparing = $this->isComparing();
$view->title = '';
if ($this->config->show_title) {
@@ -103,13 +113,35 @@ class Sparklines extends ViewDataTable
}
}
- $translations = $this->config->translations;
-
$firstRow = $data->getFirstRow();
+ $comparisons = $firstRow->getComparisons();
+
+ $originalDate = Common::getRequestVar('date');
+ $originalPeriod = Common::getRequestVar('period');
+
+ if ($this->isComparing() && !empty($comparisons)) {
+ $comparisonRows = [];
+ foreach ($comparisons->getRows() as $comparisonRow) {
+ $segment = $comparisonRow->getMetadata('compareSegment');
+ if ($segment === false) {
+ $segment = Request::getRawSegmentFromRequest() ?: '';;
+ }
- foreach ($this->config->getSparklineMetrics() as $sparklineMetric) {
+ $date = $comparisonRow->getMetadata('compareDate');
+ $period = $comparisonRow->getMetadata('comparePeriod');
+
+ $comparisonRows[$segment][$period][$date] = $comparisonRow;
+ }
+ }
+
+ foreach ($this->config->getSparklineMetrics() as $sparklineMetricIndex => $sparklineMetric) {
$column = $sparklineMetric['columns'];
$order = $sparklineMetric['order'];
+ $graphParams = $sparklineMetric['graphParams'];
+
+ if (!isset($order)) {
+ $order = 1000;
+ }
if ($column === 'label') {
continue;
@@ -120,31 +152,87 @@ class Sparklines extends ViewDataTable
continue;
}
- if (!is_array($column)) {
- $column = array($column);
- }
+ $sparklineUrlParams = array(
+ 'columns' => $column,
+ 'module' => $this->requestConfig->getApiModuleToRequest(),
+ 'action' => $this->requestConfig->getApiMethodToRequest()
+ );
+
+ if ($this->isComparing() && !empty($comparisons)) {
+ $periodObj = Factory::build($originalPeriod, $originalDate);
+
+ $sparklineUrlParams['compareSegments'] = [];
+
+ $comparePeriods = $data->getMetadata('comparePeriods');
+ $compareDates = $data->getMetadata('compareDates');
+
+ $compareSegments = $data->getMetadata('compareSegments');
+ foreach ($compareSegments as $segmentIndex => $segment) {
+ $metrics = [];
+ $seriesIndices = [];
- $values = array();
- $descriptions = array();
+ foreach ($comparePeriods as $periodIndex => $period) {
+ $date = $compareDates[$periodIndex];
- foreach ($column as $col) {
- $value = $firstRow->getColumn($col);
+ $compareRow = $comparisonRows[$segment][$period][$date];
+ $segmentPretty = $compareRow->getMetadata('compareSegmentPretty');
+ $periodPretty = $compareRow->getMetadata('comparePeriodPretty');
- if ($value === false) {
- $value = 0;
+ $columnToUse = $this->removeUniqueVisitorsIfNotEnabledForPeriod($column, $period);
+ list($compareValues, $compareDescriptions, $evolutions) = $this->getValuesAndDescriptions($compareRow, $columnToUse);
+
+ foreach ($compareValues as $i => $value) {
+ $metricInfo = [
+ 'value' => $value,
+ 'description' => $compareDescriptions[$i],
+ 'group' => $periodPretty,
+ ];
+
+ if ($periodIndex > 0
+ && isset($evolutions[$i])
+ ) {
+ $metricInfo['evolution'] = $evolutions[$i];
+ }
+
+ $metrics[] = $metricInfo;
+ }
+
+ $seriesIndices[] = DataComparisonFilter::getComparisonSeriesIndex($data, $periodIndex, $segmentIndex);
+ }
+
+ // only set the title (which is the segment) if comparing more than one segment
+ $title = count($compareSegments) > 1 ? $segmentPretty : null;
+
+ $params = array_merge($sparklineUrlParams, [
+ 'segment' => $segment,
+ 'period' => $periodObj->getLabel(),
+ 'date' => $periodObj->getRangeString(),
+ ]);
+ $this->config->addSparkline($params, $metrics, $desc = null, null, ($order * 100) + $segmentIndex, $title, $sparklineMetricIndex, $seriesIndices, $graphParams);
}
+ } else {
+ list($values, $descriptions) = $this->getValuesAndDescriptions($firstRow, $column);
- $values[] = $value;
- $descriptions[] = isset($translations[$col]) ? $translations[$col] : $col;
- }
+ $metrics = [];
+ foreach ($values as $i => $value) {
+ $newMetric = [
+ 'value' => $value,
+ 'description' => $descriptions[$i],
+ ];
- $sparklineUrlParams = array(
- 'columns' => $column,
- 'module' => $this->requestConfig->getApiModuleToRequest(),
- 'action' => $this->requestConfig->getApiMethodToRequest()
- );
+ $metrics[] = $newMetric;
+ }
- $this->config->addSparkline($sparklineUrlParams, $values, $descriptions, null, $order);
+ $evolution = null;
+
+ $computeEvolution = $this->config->compute_evolution;
+ if ($computeEvolution) {
+ $evolution = $computeEvolution(array_combine($column, $values));
+ $newMetric['evolution'] = $evolution;
+ }
+
+ $this->config->addSparkline($sparklineUrlParams, $metrics, $desc = null, $evolution, $order, $title = null, $group = $sparklineMetricIndex, $seriesIndices = null, $graphParams);
+ }
}
}
@@ -161,4 +249,44 @@ class Sparklines extends ViewDataTable
$table->applyQueuedFilters();
}
+
+ private function getValuesAndDescriptions(DataTable\Row $firstRow, $columns)
+ {
+ if (!is_array($columns)) {
+ $columns = array($columns);
+ }
+
+ $translations = $this->config->translations;
+
+ $values = array();
+ $descriptions = array();
+ $evolutions = [];
+
+ foreach ($columns as $col) {
+ $value = $firstRow->getColumn($col);
+
+ if ($value === false) {
+ $value = 0;
+ }
+
+ $evolution = $firstRow->getColumn($col . '_change'); // for comparison rows
+ if ($evolution !== false) {
+ $evolutions[] = ['percent' => ltrim($evolution, '+'), 'tooltip' => ''];
+ }
+
+ $values[] = $value;
+ $descriptions[] = isset($translations[$col]) ? $translations[$col] : $col;
+ }
+
+ return [$values, $descriptions, $evolutions];
+ }
+
+ private function removeUniqueVisitorsIfNotEnabledForPeriod($columns, $period)
+ {
+ if (SettingsPiwik::isUniqueVisitorsEnabled($period)) {
+ return $columns;
+ }
+
+ return array_diff($columns, ['nb_users', 'nb_uniq_visitors']);
+ }
}
diff --git a/plugins/CoreVisualizations/Visualizations/Sparklines/Config.php b/plugins/CoreVisualizations/Visualizations/Sparklines/Config.php
index 86944ccc5a..e5fe3da1c0 100644
--- a/plugins/CoreVisualizations/Visualizations/Sparklines/Config.php
+++ b/plugins/CoreVisualizations/Visualizations/Sparklines/Config.php
@@ -12,6 +12,7 @@ use Piwik\Common;
use Piwik\DataTable\Filter\CalculateEvolutionFilter;
use Piwik\Metrics;
use Piwik\NoAccessException;
+use Piwik\Period\Factory;
use Piwik\Period\Range;
use Piwik\Site;
use Piwik\Url;
@@ -46,6 +47,15 @@ class Config extends \Piwik\ViewDataTable\Config
*/
public $title_attributes = array();
+ /**
+ * If supplied, this function is used to compute the evolution percent displayed next to non-comparison sparkline views.
+ *
+ * The function is passed an array mapping column names with column values.
+ *
+ * @var callable
+ */
+ public $compute_evolution = null;
+
public function __construct()
{
parent::__construct();
@@ -145,12 +155,15 @@ class Config extends \Piwik\ViewDataTable\Config
* @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)
+ public function addSparklineMetric($metricName, $order = null, $graphParams = null)
{
$this->sparkline_metrics[] = array(
'columns' => $metricName,
- 'order' => $order
+ 'order' => $order,
+ 'graphParams' => $graphParams,
);
}
@@ -167,7 +180,10 @@ class Config extends \Piwik\ViewDataTable\Config
$this->sparklines[] = array(
'url' => '',
'metrics' => array(),
- 'order' => $this->getSparklineOrder($order)
+ '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),
);
}
@@ -194,62 +210,87 @@ class Config extends \Piwik\ViewDataTable\Config
* '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, $value, $description, $evolution = null, $order = null)
+ public function addSparkline($requestParamsForSparkline, $metricInfos, $description, $evolution = null, $order = null, $title = null, $group = '', $seriesIndices = null, $graphParams = null)
{
$metrics = array();
- if (is_array($value)) {
- $values = $value;
+ if ($description === null && is_array($metricInfos)) {
+ $metrics = $metricInfos;
} else {
- $values = array($value);
- }
+ $value = $metricInfos;
- if (!is_array($description)) {
- $description = array($description);
+ 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($values)) {
+ && count($requestParamsForSparkline['columns']) === count($metrics)) {
$columns = array_values($requestParamsForSparkline['columns']);
} elseif (!empty($requestParamsForSparkline['columns'])
&& is_string($requestParamsForSparkline['columns'])
- && count($values) === 1) {
+ && count($metrics) === 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);
+ 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' => $metrics,
- 'order' => $this->getSparklineOrder($order)
+ '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)) {
+ !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');
}
@@ -300,7 +341,12 @@ class Config extends \Piwik\ViewDataTable\Config
return ($a['order'] < $b['order']) ? -1 : 1;
});
- return $this->sparklines;
+ $sparklines = [];
+ foreach ($this->sparklines as $sparkline) {
+ $group = $sparkline['group'];
+ $sparklines[$group][] = $sparkline;
+ }
+ return $sparklines;
}
private function getSparklineOrder($order)
@@ -390,7 +436,13 @@ class Config extends \Piwik\ViewDataTable\Config
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);
+ 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;
diff --git a/plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.js b/plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.js
index 353a5faebc..c7f2c41dc6 100644
--- a/plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.js
+++ b/plugins/CoreVisualizations/angularjs/single-metric-view/single-metric-view.component.js
@@ -164,7 +164,7 @@
function getLastPeriodDate() {
var RangePeriod = piwikPeriods.get('range');
var result = RangePeriod.getLastNRange(piwik.period, 2, piwik.currentDateString).startDate;
- return $.datepicker.formatDate('yy-mm-dd', result);
+ return piwikPeriods.format(result);
}
function setWidgetTitle() {
@@ -269,7 +269,7 @@
function getPastPeriodStr() {
var startDate = piwikPeriods.get('range').getLastNRange(piwik.period, 2, piwik.currentDateString).startDate;
var dateRange = piwikPeriods.get(piwik.period).parse(startDate).getDateRange();
- return $.datepicker.formatDate('yy-mm-dd', dateRange[0]) + ',' + $.datepicker.formatDate('yy-mm-dd', dateRange[1]);
+ return piwikPeriods.format(dateRange[0]) + ',' + piwikPeriods.format(dateRange[1]);
}
function isIdGoalSet() {
diff --git a/plugins/CoreVisualizations/javascripts/jqplot.js b/plugins/CoreVisualizations/javascripts/jqplot.js
index a509f401a6..c53cdcb493 100644
--- a/plugins/CoreVisualizations/javascripts/jqplot.js
+++ b/plugins/CoreVisualizations/javascripts/jqplot.js
@@ -634,9 +634,7 @@ function rowEvolutionGetMetricNameFromRow(tr)
* Sets the colors used to render this graph.
*/
_setColors: function () {
- var colorManager = piwik.ColorManager,
- seriesColorNames = ['series1', 'series2', 'series3', 'series4', 'series5',
- 'series6', 'series7', 'series8', 'series9', 'series10'];
+ var colorManager = piwik.ColorManager;
var viewDataTable = $('#' + this.workingDivId).data('uiControlObject').param['viewDataTable'];
@@ -651,11 +649,31 @@ function rowEvolutionGetMetricNameFromRow(tr)
var namespace = graphType + '-graph-colors';
- this.jqplotParams.seriesColors = colorManager.getColors(namespace, seriesColorNames, true);
+ this._setSeriesColors(namespace);
+
this.jqplotParams.grid.background = colorManager.getColor(namespace, 'grid-background');
this.jqplotParams.grid.borderColor = colorManager.getColor(namespace, 'grid-border');
this.tickColor = colorManager.getColor(namespace, 'ticks');
this.singleMetricColor = colorManager.getColor(namespace, 'single-metric-label')
+ },
+
+ _setSeriesColors: function (namespace) {
+ var colorManager = piwik.ColorManager,
+ seriesColorNames = ['series0', 'series1', 'series2', 'series3', 'series4', 'series5',
+ 'series6', 'series7', 'series8', 'series9', 'series10'];
+
+ var comparisonService = piwikHelper.getAngularDependency('piwikComparisonsService');
+ if (comparisonService.isComparing() && typeof this.jqplotParams.series[0].seriesIndex !== 'undefined') {
+ namespace = 'comparison-series-color';
+
+ seriesColorNames = [];
+ this.jqplotParams.series.forEach(function (s) {
+ var seriesColorName = comparisonService.getSeriesColorName(s.seriesIndex, s.metricIndex);
+ seriesColorNames.push(seriesColorName);
+ });
+ }
+
+ this.jqplotParams.seriesColors = colorManager.getColors(namespace, seriesColorNames, true);
}
});
@@ -1040,7 +1058,9 @@ RowEvolutionSeriesToggle.prototype.beforeReplot = function () {
c.markerRenderer.init();
var position = series.gridData[tick];
- c.markerRenderer.draw(position[0], position[1], c.piwikHighlightCanvas._ctx);
+ if (typeof position !== 'undefined') {
+ c.markerRenderer.draw(position[0], position[1], c.piwikHighlightCanvas._ctx);
+ }
}
}
diff --git a/plugins/CoreVisualizations/javascripts/jqplotBarGraph.js b/plugins/CoreVisualizations/javascripts/jqplotBarGraph.js
index cd21308f73..24af4c2e9d 100644
--- a/plugins/CoreVisualizations/javascripts/jqplotBarGraph.js
+++ b/plugins/CoreVisualizations/javascripts/jqplotBarGraph.js
@@ -22,6 +22,9 @@
_setJqplotParameters: function (params) {
JqplotGraphDataTable.prototype._setJqplotParameters.call(this, params);
+ var barMargin = this.data[0].length > 10 ? 2 : 10;
+ var minBarWidth = 10;
+
this.jqplotParams.seriesDefaults = {
renderer: $.jqplot.BarRenderer,
rendererOptions: {
@@ -29,7 +32,7 @@
shadowDepth: 2,
shadowAlpha: .2,
fillToZero: true,
- barMargin: this.data[0].length > 10 ? 2 : 10
+ barMargin: barMargin
}
};
@@ -48,6 +51,18 @@
this.jqplotParams.canvasLegend = {
show: true
};
+
+ var comparisonService = piwikHelper.getAngularDependency('piwikComparisonsService');
+ if (comparisonService.isComparing()) {
+ var seriesCount = this.jqplotParams.series.length;
+ var dataCount = this.data[0].length;
+
+ var totalBars = seriesCount * dataCount;
+ var totalMinWidth = (minBarWidth + barMargin) * totalBars + 50;
+
+ this.$element.find('.piwik-graph').css('min-width', totalMinWidth + 'px');
+ this.$element.css('overflow-x', 'scroll');
+ }
},
_bindEvents: function () {
diff --git a/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js b/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js
index b77add4962..012e5e8bc1 100644
--- a/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js
+++ b/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js
@@ -96,19 +96,53 @@
.on('jqplotPiwikTickOver', function (e, tick) {
lastTick = tick;
var label;
- if (typeof self.jqplotParams.axes.xaxis.labels != 'undefined') {
- label = self.jqplotParams.axes.xaxis.labels[tick];
- } else {
- label = self.jqplotParams.axes.xaxis.ticks[tick];
- }
- var text = [];
- for (var d = 0; d < self.data.length; d++) {
- var value = self.formatY(self.data[d][tick], d);
+ var dataByAxis = {};
+ for (var d = 0; d < self.data.length; ++d) {
+ var valueUnformatted = self.data[d][tick];
+ if (typeof valueUnformatted === 'undefined' || valueUnformatted === null) {
+ continue;
+ }
+
+ var axis = self.jqplotParams.series[d]._xaxis || 'xaxis';
+ if (!dataByAxis[axis]) {
+ dataByAxis[axis] = [];
+ }
+
+ var value = self.formatY(valueUnformatted, d);
var series = self.jqplotParams.series[d].label;
- text.push('<strong>' + value + '</strong> ' + piwikHelper.htmlEntities(series));
+
+ var seriesColor = self.jqplotParams.seriesColors[d];
+
+ dataByAxis[axis].push('<span class="tooltip-series-color" style="background-color: ' + seriesColor + ';"/>' + '<strong>' + value + '</strong> ' + piwikHelper.htmlEntities(series));
+ }
+
+ var xAxisCount = 0;
+ Object.keys(self.jqplotParams.axes).forEach(function (axis) {
+ if (axis.substring(0, 1) === 'x') {
+ ++xAxisCount;
+ }
+ });
+
+ var content = '';
+ for (var i = 0; i < xAxisCount; ++i) {
+ var axisName = i === 0 ? 'xaxis' : 'x' + (i + 1) + 'axis';
+ if (!dataByAxis[axisName] || !dataByAxis[axisName].length) {
+ continue;
+ }
+
+ if (typeof self.jqplotParams.axes[axisName].labels != 'undefined') {
+ label = self.jqplotParams.axes[axisName].labels[tick];
+ } else {
+ label = self.jqplotParams.axes[axisName].ticks[tick];
+ }
+
+ if (typeof label === 'undefined') { // sanity check
+ continue;
+ }
+
+ content += '<h3 class="evolution-tooltip-header">'+piwikHelper.htmlEntities(label)+'</h3>'+dataByAxis[axisName].join('<br />');
}
- var content = '<h3>'+piwikHelper.htmlEntities(label)+'</h3>'+text.join('<br />');
$(this).tooltip({
track: true,
diff --git a/plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less b/plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less
index ff2118b952..15edf722c0 100644
--- a/plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less
+++ b/plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less
@@ -23,4 +23,18 @@
}
.widget .dataTableVizBar .jqplot-graph {
padding: 0 10px 10px 10px;
+}
+
+.tooltip-series-color {
+ display: inline-block;
+ width: 11px;
+ height: 11px;
+ border-radius: 6px;
+ margin-right: 3px;
+ position: relative;
+ top: 1px;
+}
+
+.ui-tooltip h3.evolution-tooltip-header {
+ margin-top: 4px;
} \ No newline at end of file
diff --git a/plugins/CoreVisualizations/templates/_dataTableViz_htmlTable.twig b/plugins/CoreVisualizations/templates/_dataTableViz_htmlTable.twig
index 38c2101168..a9ac3f4dae 100644
--- a/plugins/CoreVisualizations/templates/_dataTableViz_htmlTable.twig
+++ b/plugins/CoreVisualizations/templates/_dataTableViz_htmlTable.twig
@@ -19,11 +19,13 @@
</tr>
{% endif %}
{% else %}
+ {% set rowIndex = properties.filter_offset|default(0) + 1 %}
{%- for rowId, row in dataTable.getRows() -%}
{%- set rowHasSubtable = not subtablesAreDisabled and row.getIdSubDataTable() and properties.subtable_controller_action is not null -%}
{%- set rowSubtableId = row.getMetadata('idsubdatatable_in_db')|default(row.getIdSubDataTable()) -%}
{%- set isSummaryRow = rowId == constant('Piwik\\DataTable::ID_SUMMARY_ROW') or row.getMetadata('is_summary') -%}
{%- set shouldHighlightRow = isSummaryRow and properties.highlight_summary_row -%}
+ {% set dimensions = dataTable.getMetadata('dimensions')|default([]) %}
{# display this row if it doesn't have a subtable or if we don't replace the row with the subtable #}
{%- set showRow = subtablesAreDisabled
@@ -36,21 +38,38 @@
{% if row.getMetadata('segment') is not false %} data-segment-filter="{{ row.getMetadata('segment')|e('html_attr') }}"{% endif %}
{% if row.getMetadata('url') is not false %} data-url-label="{{ row.getMetadata('url')|rawSafeDecoded }}"{% endif %}
data-row-metadata="{{ row.getMetadata|json_encode|e('html_attr') }}"
- class="{{ row.getMetadata('css_class') }} {% if rowHasSubtable %}subDataTable{% endif %}{% if shouldHighlightRow %} highlight{% endif %}{% if isSummaryRow %} summaryRow{% endif %}"
+ class="{{ row.getMetadata('css_class') }} {% if rowHasSubtable %}subDataTable{% endif %}{% if shouldHighlightRow %} highlight{% endif %}{% if isSummaryRow %} summaryRow{% endif %} {% if isComparing %}parentComparisonRow{% endif %}"
{% if rowHasSubtable %}title="{{ 'CoreHome_ClickRowToExpandOrContract'|translate }}"{% endif %}>
{% for column in properties.columns_to_display %}
{% set cellAttributes = visualization.getCellHtmlAttributes(row, column) %}
- <td {% if cellAttributes is not empty %}{% for name, value in cellAttributes %}{{ name|e('html') }}="{{ value|e('html_attr') }}" {% endfor %}{% endif %}>
+ <td class="{% if column =='label' or column in dimensions %}label{% else %}column{% endif %} {{ cellAttributes.class|default|e('html_attr') }}"
+ {% if cellAttributes is not empty %}{% for name, value in cellAttributes %}{{ name|e('html') }}="{{ value|e('html_attr') }}" {% endfor %}{% endif %}
+ >
+ {% if isComparing and column == 'label' %}
+ <span class="prefix-numeral">{{ rowIndex }}.</span>
+ {% endif %}
+
{% include "@CoreHome/_dataTableCell.twig" with properties %}
</td>
{% endfor %}
</tr>
+
+ {% if row.getComparisons() %}
+ {% include "@CoreVisualizations/_dataTableViz_htmlTable_comparisons.twig" with {
+ 'comparedRow': row,
+ 'dataTable': row.getComparisons(),
+ 'rootDataTable': dataTable,
+ 'dimensions': dimensions,
+ } %}
+ {% endif %}
{% endif %}
{# display subtable if present and showing expanded datatable #}
{% if properties.show_expanded|default(false) and rowHasSubtable %}
{% include "@CoreVisualizations/_dataTableViz_htmlTable.twig" with {'dataTable': row.getSubtable(), 'idSubtable': rowSubtableId} %}
{% endif %}
+
+ {% set rowIndex = rowIndex + 1 %}
{%- endfor -%}
{% if dataTable.getTotalsRow and properties.show_totals_row %}
{% set row = dataTable.getTotalsRow %}
@@ -58,7 +77,7 @@
<tr class="{{ row.getMetadata('css_class') }} totalsRow"
title="Total values for this table">
{% for column in properties.columns_to_display %}
- <td>
+ <td class="{% if column =='label' %}label{% else %}column{% endif %}">
{% include "@CoreHome/_dataTableCell.twig" with properties %}
</td>
{% endfor %}
diff --git a/plugins/CoreVisualizations/templates/_dataTableViz_htmlTable_comparisons.twig b/plugins/CoreVisualizations/templates/_dataTableViz_htmlTable_comparisons.twig
new file mode 100644
index 0000000000..fec4652ed4
--- /dev/null
+++ b/plugins/CoreVisualizations/templates/_dataTableViz_htmlTable_comparisons.twig
@@ -0,0 +1,83 @@
+{% if properties.columns_to_display is not empty %}
+{% set lastSeenComparePeriod = false %}
+{% set comparedPeriodPretty = dataTable.getFirstRow().getMetadata('comparePeriodPretty') %}
+{% set isComparingSegments = rootDataTable.getMetadata('compareSegments')|default([])|length > 1 %}
+{% set isComparingPeriods = rootDataTable.getMetadata('comparePeriods')|default([])|length > 1 %}
+{%- for rowId, row in dataTable.getRows() -%}
+ {% set comparePeriod = row.getMetadata('comparePeriodPretty') %}
+ {% if lastSeenComparePeriod != comparePeriod and isComparingSegments and isComparingPeriods %}
+ <tr class="comparePeriod">
+ <td class="label">
+ {{ comparePeriod }}
+ </td>
+
+ {# add extra empty columns for sticky column scrolling to work #}
+ {% for column in properties.columns_to_display %}
+ {% if column != 'label' %}
+ <td class="column">&nbsp;</td>
+ {% endif %}
+ {% endfor %}
+ </tr>
+ {% endif %}
+ {% set overrideParams = {} %}
+
+ {% if row.getMetadata('compareSegment')|default is not empty %}{% set overrideParams = overrideParams|merge({ segment: row.getMetadata('compareSegment'), compareSegments: '' }) %}{% endif %}
+ {% if row.getMetadata('comparePeriod')|default is not empty %}{% set overrideParams = overrideParams|merge({ period: row.getMetadata('comparePeriod'), comparePeriods: '' }) %}{% endif %}
+ {% if row.getMetadata('compareDate')|default is not empty %}{% set overrideParams = overrideParams|merge({ date: row.getMetadata('compareDate'), compareDates: '' }) %}{% endif %}
+ {% set idSubtable = row.getMetadata('idsubdatatable') %}
+ {% set seriesIndex = loop.index0 %}
+ <tr
+ {% if idSubtable != false %}data-idsubtable="{{ idSubtable|e('html_attr') }}"{% endif %}
+ data-comparison-series="{{ seriesIndex }}"
+ class="comparisonRow"
+ data-param-override="{{ overrideParams|json_encode|e('html_attr') }}" data-label="{{ comparedRow.getColumn('label')|e('html_attr') }}"
+ {% if row.getMetadata('segment') is not false %}data-segment-filter="{{ row.getMetadata('segment')|e('html_attr') }}"{% endif %}
+ >
+ <td class="label">
+ {% if isComparingSegments %}
+ {%- set comparisonLabel = row.getMetadata('compareSegmentPretty') -%}
+ {% else %}
+ {%- set comparisonLabel = comparePeriod -%}
+ {% endif %}
+ <span class="label">{{ comparisonLabel }}</span>
+ </td>
+ {% for dimension in dimensions %}
+ {% if loop.index0 != 0 %}
+ <td class="label">
+ &nbsp;
+ </td>
+ {% endif %}
+ {% endfor %}
+ {% for column in properties.columns_to_display %}
+ {% if column != 'label' and column not in dimensions %}
+ {% set columnValue = row.getColumn(column) %}
+ <td class="column">
+ {% set rowComparisonTotals = comparisonTotals[seriesIndex].totals|default(null) %}
+
+ {% set comparisonTooltipSuffix = '' %}
+ {% set columnChange = false %}
+ {% if row.getColumn(column ~ '_change')|default is not empty %}
+ {% set columnChange = row.getColumn(column ~ '_change')|default('+0%') %}
+ {% set comparisonTooltipSuffix = 'General_ComparisonRatioTooltip'|translate(columnChange, row.getMetadata('compareSegmentPretty'), comparedPeriodPretty) %}
+ {% endif %}
+
+ {% include "@CoreVisualizations/_dataTableViz_htmlTable_ratio.twig" with {
+ 'changePercentage': columnChange,
+ 'totals': rowComparisonTotals,
+ 'label': comparedRow.getColumn('label'),
+ 'labelColumn': properties.columns_to_display|first,
+ 'changePercantage': columnChange,
+ 'forceZero': true,
+ 'tooltipSuffix': comparisonTooltipSuffix,
+ 'translations': properties.translations,
+ 'segmentTitlePretty': row.getMetadata('compareSegmentPretty'),
+ 'periodTitlePretty': row.getMetadata('comparePeriodPretty')
+ } %}
+ <span class="value">{{ columnValue|default(0)|number(2,0)|rawSafeDecoded }}</span>
+ </td>
+ {% endif %}
+ {% endfor %}
+ </tr>
+ {% set lastSeenComparePeriod = comparePeriod %}
+{%- endfor -%}
+{% endif %} \ No newline at end of file
diff --git a/plugins/CoreVisualizations/templates/_dataTableViz_htmlTable_ratio.twig b/plugins/CoreVisualizations/templates/_dataTableViz_htmlTable_ratio.twig
new file mode 100644
index 0000000000..8d14bb126d
--- /dev/null
+++ b/plugins/CoreVisualizations/templates/_dataTableViz_htmlTable_ratio.twig
@@ -0,0 +1,26 @@
+{% if column in properties.report_ratio_columns and (column in totals|keys or forceZero|default) -%}
+ {% set reportTotal = totals[column]|default(0) %}
+ {% if siteTotalRow|default is not empty %}
+ {% set siteTotal = siteTotalRow.getColumn(column) %}
+ {% elseif siteSummary is defined and siteSummary is not empty and siteSummary.getFirstRow %}
+ {% set siteTotal = siteSummary.getFirstRow.getColumn(column) %}
+ {% else %}
+ {% set siteTotal = 0 %}
+ {% endif %}
+
+ {% set rowPercentage = row.getColumn(column)|percentage(reportTotal, 1) %}
+ {% set metricTitle = translations[column]|default(column) %}
+
+ {% set reportRatioTooltip = 'General_ReportRatioTooltip'|translate(label, rowPercentage|e('html_attr'), reportTotal|e('html_attr'), metricTitle|e('html_attr'), '"' ~ segmentTitlePretty ~ '"', translations[labelColumn]|default(labelColumn)|e('html_attr')) %}
+
+ {% if siteTotal and siteTotal > reportTotal %}
+ {% set totalPercentage = row.getColumn(column)|percentage(siteTotal, 1) %}
+ {% set totalRatioTooltip = 'General_TotalRatioTooltip'|translate(totalPercentage, siteTotal|number(2,0), metricTitle, periodTitlePretty) %}
+ {% else %}
+ {% set totalRatioTooltip = '' %}
+ {% endif %}
+
+ <span class="ratio"
+ title="{{ reportRatioTooltip|rawSafeDecoded|raw }} {{ totalRatioTooltip|rawSafeDecoded|e('html_attr') }}{% if tooltipSuffix|default is not empty %}<br/><br/> {{ tooltipSuffix|rawSafeDecoded|e('html_attr') }}{% endif %}"
+ >&nbsp;{{ rowPercentage }} {% if changePercantage|default is not empty %}({{ changePercentage }}){% endif %}</span>
+{%- endif %}
diff --git a/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig b/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig
index 5c8390d914..e1932c523b 100644
--- a/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig
+++ b/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig
@@ -10,12 +10,23 @@
<div class="row">
<div class="col m6">
{% endif %}
-
- {% for key, sparkline in sparklines %}
- {% if key is even %}
+ {% if sparklines|length == 1 %}
+ {% for key, sparkline in sparklines|first %}
+ {% if loop.index0 is even %}
{{ macros.singleSparkline(sparkline, allMetricsDocumentation, areSparklinesLinkable) }}
{% endif %}
{% endfor %}
+ {% else %}
+ {% for group in sparklines %}
+ {% if loop.index0 is even %}
+ <div>
+ {% for key, sparkline in group %}
+ {{ macros.singleSparkline(sparkline, allMetricsDocumentation, areSparklinesLinkable) }}
+ {% endfor %}
+ </div>
+ {% endif %}
+ {% endfor %}
+ {% endif %}
{% if not isWidget %}
<br style="clear:left"/>
@@ -23,11 +34,23 @@
<div class="col m6">
{% endif %}
+ {% if sparklines|length == 1 %}
{% for key, sparkline in sparklines %}
- {% if key is odd %}
+ {% if loop.index0 is odd %}
{{ macros.singleSparkline(sparkline, allMetricsDocumentation, areSparklinesLinkable) }}
{% endif %}
{% endfor %}
+ {% else %}
+ {% for group in sparklines %}
+ {% if loop.index0 is odd %}
+ <div>
+ {% for key, sparkline in group %}
+ {{ macros.singleSparkline(sparkline, allMetricsDocumentation, areSparklinesLinkable) }}
+ {% endfor %}
+ </div>
+ {% endif %}
+ {% endfor %}
+ {% endif %}
<br style="clear:left"/>
diff --git a/plugins/CoreVisualizations/templates/macros.twig b/plugins/CoreVisualizations/templates/macros.twig
index 7562b88f78..bc754f2c19 100644
--- a/plugins/CoreVisualizations/templates/macros.twig
+++ b/plugins/CoreVisualizations/templates/macros.twig
@@ -1,36 +1,52 @@
+
+{% macro sparklineEvolution(evolution) %}
+ {% set evolutionPretty = evolution.percent %}
+
+ {% if evolution.percent < 0 %}
+ {% set evolutionClass = 'negative-evolution' %}
+ {% set evolutionIcon = 'arrow_down.png' %}
+ {% elseif evolution.percent == 0 %}
+ {% set evolutionClass = 'neutral-evolution' %}
+ {% set evolutionIcon = 'stop.png' %}
+ {% else %}
+ {% set evolutionClass = 'positive-evolution' %}
+ {% set evolutionIcon = 'arrow_up.png' %}
+ {% set evolutionPretty = '+' ~ evolution.percent %}
+ {% endif %}
+
+ <span class="metricEvolution" title="{{ evolution.tooltip|rawSafeDecoded|e('html_attr') }}">
+ <img style="padding-right:4px" src="plugins/MultiSites/images/{{ evolutionIcon }}"/>
+ <strong class="{{ evolutionClass }}">{{ evolutionPretty }}</strong></span>
+{% endmacro %}
+
{% macro singleSparkline(sparkline, allMetricsDocumentation, areSparklinesLinkable) %}
- <div class="sparkline {% if areSparklinesLinkable is defined and not areSparklinesLinkable %}notLinkable{% endif %}">
- {% if sparkline.url %}{{ sparkline(sparkline.url)|raw }}{% endif %}
+ <div class="sparkline {% if areSparklinesLinkable is defined and not areSparklinesLinkable %}notLinkable{% endif %}"
+ {% if sparkline.seriesIndices|default is not empty %}data-series-indices="{{ sparkline.seriesIndices|json_encode|e('html_attr') }}"{% endif %}
+ {% if sparkline.graphParams|default is not empty %}data-graph-params="{{ sparkline.graphParams|json_encode|e('html_attr') }}"{% endif %}
+ >
+ <div>
+ {% if sparkline.title|default is not empty %}<h6 class="sparkline-title" title="{{ sparkline.title|rawSafeDecoded|e('html_attr') }}">{{ sparkline.title }}</h6>{% endif %}
+ {% if sparkline.url %}{{ sparkline(sparkline.url)|raw }}{% endif %}
+ </div>
<div>
- {% for metric in sparkline.metrics %}
- <span {% if allMetricsDocumentation[metric.column] is defined and allMetricsDocumentation[metric.column] %}title="{{ allMetricsDocumentation[metric.column] }}"{% endif %}>
- {% if '%s' in metric.description -%}
- {{ metric.description|translate("<strong>"~metric.value~"</strong>")|raw }}
- {%- else %}
- <strong>{{ metric.value }}</strong> {{ metric.description }}
- {%- endif %}{% if not loop.last %}, {% endif %}
- </span>
+ {% for groupName, group in sparkline.metrics %}
+ {% if groupName is not empty %}<span class="metric-group-title">{{ groupName }}</span>{% endif %}
+ {% for metric in group %}
+ <span class="sparkline-metrics" {% if allMetricsDocumentation[metric.column] is defined and allMetricsDocumentation[metric.column] %}title="{{ allMetricsDocumentation[metric.column] }}"{% endif %}>
+ {% if '%s' in metric.description -%}
+ {{ metric.description|translate("<strong>"~metric.value~"</strong>")|raw }}
+ {%- else %}
+ <strong>{{ metric.value }}</strong> {{ metric.description }}
+ {%- endif %}{% if not loop.last %}, {% endif %}
+ </span>
+ {% if metric.evolution is defined %}
+ {{ _self.sparklineEvolution(metric.evolution) }}
+ {% endif %}
+ {% endfor %}
{% endfor %}
{% if sparkline.evolution is defined %}
-
- {% set evolutionPretty = sparkline.evolution.percent %}
-
- {% if sparkline.evolution.percent < 0 %}
- {% set evolutionClass = 'negative-evolution' %}
- {% set evolutionIcon = 'arrow_down.png' %}
- {% elseif sparkline.evolution.percent == 0 %}
- {% set evolutionClass = 'neutral-evolution' %}
- {% set evolutionIcon = 'stop.png' %}
- {% else %}
- {% set evolutionClass = 'positive-evolution' %}
- {% set evolutionIcon = 'arrow_up.png' %}
- {% set evolutionPretty = '+' ~ sparkline.evolution.percent %}
- {% endif %}
-
- <span class="metricEvolution" title="{{ sparkline.evolution.tooltip }}"><img
- style="padding-right:4px" src="plugins/MultiSites/images/{{ evolutionIcon }}"/>
- <strong class="{{ evolutionClass }}">{{ evolutionPretty }}</strong></span>
+ {{ _self.sparklineEvolution(sparkline.evolution) }}
{% endif %}
</div>
</div>
diff --git a/plugins/CoreVisualizations/tests/Integration/SparklinesConfigTest.php b/plugins/CoreVisualizations/tests/Integration/SparklinesConfigTest.php
index 33fb6be6ab..6847ac7b2c 100644
--- a/plugins/CoreVisualizations/tests/Integration/SparklinesConfigTest.php
+++ b/plugins/CoreVisualizations/tests/Integration/SparklinesConfigTest.php
@@ -65,12 +65,18 @@ class SparklinesConfigTest extends IntegrationTestCase
$expectedSparkline = array(
'url' => '?period=day&date=2012-03-06,2012-04-04&idSite=1&module=CoreHome&action=renderMe&viewDataTable=sparkline',
'metrics' => array (
- array ('column' => '', 'value' => 10, 'description' => 'Visits'),
+ '' => [
+ array ('value' => 10, 'description' => 'Visits', 'column' => ''),
+ ],
),
- 'order' => 999
+ 'order' => 999,
+ 'title' => null,
+ 'group' => '',
+ 'seriesIndices' => null,
+ 'graphParams' => null,
);
- $this->assertSame(array($expectedSparkline), $this->config->getSortedSparklines());
+ $this->assertSame(array($expectedSparkline), $this->config->getSortedSparklines()['']);
}
public function test_addSparkline_shouldAddAMinimalSparklineWithOneValueAndUseDefaultOrderWithColumn()
@@ -79,10 +85,10 @@ class SparklinesConfigTest extends IntegrationTestCase
$params['columns'] = 'nb_visits';
$this->config->addSparkline($params, $value = 10, $description = 'Visits');
- $expectedSparkline = array('column' => 'nb_visits', 'value' => 10, 'description' => 'Visits');
+ $expectedSparkline = array('value' => 10, 'description' => 'Visits', 'column' => 'nb_visits');
$sparklines = $this->config->getSortedSparklines();
- $this->assertSame(array($expectedSparkline), $sparklines[0]['metrics']);
+ $this->assertSame(array($expectedSparkline), $sparklines[''][0]['metrics']['']);
}
public function test_addSparkline_shouldAddSparklineWithMultipleValues()
@@ -92,9 +98,9 @@ class SparklinesConfigTest extends IntegrationTestCase
$sparklines = $this->config->getSortedSparklines();
$this->assertSame(array (
- array ('column' => '', 'value' => 10, 'description' => 'Visits'),
- array ('column' => '', 'value' => 20, 'description' => 'Actions'),
- ), $sparklines[0]['metrics']);
+ array ('value' => 10, 'description' => 'Visits', 'column' => ''),
+ array ('value' => 20, 'description' => 'Actions', 'column' => ''),
+ ), $sparklines[''][0]['metrics']['']);
}
public function test_addSparkline_shouldAddSparklinesMultipleValuesWithColumns()
@@ -105,12 +111,12 @@ class SparklinesConfigTest extends IntegrationTestCase
$this->config->addSparkline($params, $values = array(10, 20), $description = array('Visits', 'Actions'));
$expectedSparkline = array(
- array ('column' => 'nb_visits', 'value' => 10, 'description' => 'Visits'),
- array ('column' => 'nb_actions', 'value' => 20, 'description' => 'Actions')
+ array ('value' => 10, 'description' => 'Visits', 'column' => 'nb_visits'),
+ array ('value' => 20, 'description' => 'Actions', 'column' => 'nb_actions')
);
$sparklines = $this->config->getSortedSparklines();
- $this->assertSame($expectedSparkline, $sparklines[0]['metrics']);
+ $this->assertSame($expectedSparkline, $sparklines[''][0]['metrics']['']);
}
/**
@@ -133,7 +139,7 @@ class SparklinesConfigTest extends IntegrationTestCase
$this->assertSame(array (
'percent' => '-52.4%',
'tooltip' => '1 visit compared to 2 visits'
- ), $sparklines[0]['evolution']);
+ ), $sparklines[''][0]['evolution']);
}
public function test_addSparkline_shouldAddOrder()
@@ -142,7 +148,7 @@ class SparklinesConfigTest extends IntegrationTestCase
$sparklines = $this->config->getSortedSparklines();
- $this->assertSame(42, $sparklines[0]['order']);
+ $this->assertSame(42, $sparklines[''][0]['order']);
}
public function test_addSparkline_shouldBeAbleToBuildSparklineUrlBasedOnGETparams()
@@ -154,7 +160,166 @@ class SparklinesConfigTest extends IntegrationTestCase
$sparklines = $this->config->getSortedSparklines();
- $this->assertSame('?columns=nb_visits&viewDataTable=sparkline&date=2012-03-06,2012-04-04', $sparklines[0]['url']);
+ $this->assertSame('?columns=nb_visits&viewDataTable=sparkline&date=2012-03-06,2012-04-04', $sparklines[''][0]['url']);
+ }
+
+ public function test_addSparkline_shouldAddSparklinesWithGroups()
+ {
+ $this->config->addSparkline($this->sparklineParams(), $value = 10, $description = 'Visits', $evolution = null, $order = '4', $title = 'title1', $group = 'one');
+ $this->config->addSparkline($this->sparklineParams(), $value = 11, $description = 'Visits1', $evolution = null, $order = '1', $title = 'title2', $group = 'one');
+ $this->config->addSparkline($this->sparklineParams(), $value = 12, $description = 'Visits2', $evolution = null, $order = '3', $title = 'title3', $group = 'two');
+ $this->config->addSparkline($this->sparklineParams(), $value = 13, $description = 'Visits3', $evolution = null, $order = '6', $title = 'title4', $group = 'two');
+
+ $sparklines = $this->config->getSortedSparklines();
+ $expectedSparklines = [
+ 'one' => [
+ [
+ 'url' => '?period=day&date=2012-03-06,2012-04-04&idSite=1&module=CoreHome&action=renderMe&viewDataTable=sparkline',
+ 'metrics' => [
+ '' => [
+ 0 => [
+ 'value' => 11,
+ 'description' => 'Visits1',
+ 'column' => '',
+ ],
+ ],
+ ],
+ 'order' => 1,
+ 'title' => 'title2',
+ 'group' => 'one',
+ 'seriesIndices' => null,
+ 'graphParams' => null,
+ ],
+ [
+ 'url' => '?period=day&date=2012-03-06,2012-04-04&idSite=1&module=CoreHome&action=renderMe&viewDataTable=sparkline',
+ 'metrics' => [
+ '' => [
+ 0 => [
+ 'value' => 10,
+ 'description' => 'Visits',
+ 'column' => '',
+ ],
+ ],
+ ],
+ 'order' => 4,
+ 'title' => 'title1',
+ 'group' => 'one',
+ 'seriesIndices' => null,
+ 'graphParams' => null,
+ ],
+ ],
+ 'two' => [
+ [
+ 'url' => '?period=day&date=2012-03-06,2012-04-04&idSite=1&module=CoreHome&action=renderMe&viewDataTable=sparkline',
+ 'metrics' => [
+ '' => [
+ 0 => [
+ 'value' => 12,
+ 'description' => 'Visits2',
+ 'column' => '',
+ ],
+ ],
+ ],
+ 'order' => 3,
+ 'title' => 'title3',
+ 'group' => 'two',
+ 'seriesIndices' => null,
+ 'graphParams' => null,
+ ],
+ [
+ 'url' => '?period=day&date=2012-03-06,2012-04-04&idSite=1&module=CoreHome&action=renderMe&viewDataTable=sparkline',
+ 'metrics' => [
+ '' => [
+ 0 => [
+ 'value' => 13,
+ 'description' => 'Visits3',
+ 'column' => '',
+ ],
+ ],
+ ],
+ 'order' => 6,
+ 'title' => 'title4',
+ 'group' => 'two',
+ 'seriesIndices' => null,
+ 'graphParams' => null,
+ ],
+ ],
+ ];
+
+ $this->assertSame($expectedSparklines, $sparklines);
+ }
+
+ public function test_addSparkline_shouldAddSparklineMetricsWithGroups()
+ {
+ $metricInfos = [
+ [
+ 'value' => 'v1',
+ 'description' => 'd1',
+ 'group' => 'g1',
+ ],
+ [
+ 'value' => 'v2',
+ 'description' => 'd3',
+ 'group' => 'g1',
+ ],
+ [
+ 'value' => 'v3',
+ 'description' => 'd3',
+ 'group' => 'g3',
+ ],
+ [
+ 'value' => 'v4',
+ 'description' => 'd4',
+ 'group' => 'g1',
+ ],
+ ];
+ $this->config->addSparkline($this->sparklineParams(), $metricInfos, $description = null);
+
+ $sparklines = $this->config->getSortedSparklines();
+ $expectedSparklines = [
+ '' => [
+ [
+ 'url' => '?period=day&date=2012-03-06,2012-04-04&idSite=1&module=CoreHome&action=renderMe&viewDataTable=sparkline',
+ 'metrics' => [
+ 'g1' => [
+ 0 => [
+ 'value' => 'v1',
+ 'description' => 'd1',
+ 'group' => 'g1',
+ 'column' => '',
+ ],
+ 1 => [
+ 'value' => 'v2',
+ 'description' => 'd3',
+ 'group' => 'g1',
+ 'column' => '',
+ ],
+ 2 => [
+ 'value' => 'v4',
+ 'description' => 'd4',
+ 'group' => 'g1',
+ 'column' => '',
+ ],
+ ],
+ 'g3' => [
+ 0 => [
+ 'value' => 'v3',
+ 'description' => 'd3',
+ 'group' => 'g3',
+ 'column' => '',
+ ],
+ ],
+ ],
+ 'order' => 999,
+ 'title' => null,
+ 'group' => '',
+ 'seriesIndices' => null,
+ 'graphParams' => null,
+ ],
+ ],
+ ];
+
+ $this->assertSame($expectedSparklines, $sparklines);
}
private function sparklineParams($params = array())
diff --git a/plugins/CoreVisualizations/tests/Unit/SparklinesConfigTest.php b/plugins/CoreVisualizations/tests/Unit/SparklinesConfigTest.php
index 1134df2f5c..e4759845fa 100644
--- a/plugins/CoreVisualizations/tests/Unit/SparklinesConfigTest.php
+++ b/plugins/CoreVisualizations/tests/Unit/SparklinesConfigTest.php
@@ -49,9 +49,9 @@ class SparklinesConfigTest extends \PHPUnit_Framework_TestCase
$this->addFewSparklines();
$this->assertSame(array(
- array('columns' => 'nb_visits', 'order' => null),
- array('columns' => 'nb_unique_visitors', 'order' => 99),
- array('columns' => array('nb_downloads', 'nb_outlinks'), 'order' => null),
+ array('columns' => 'nb_visits', 'order' => null, 'graphParams' => null),
+ array('columns' => 'nb_unique_visitors', 'order' => 99, 'graphParams' => null),
+ array('columns' => array('nb_downloads', 'nb_outlinks'), 'order' => null, 'graphParams' => null),
), $this->config->getSparklineMetrics());
}
@@ -62,8 +62,8 @@ class SparklinesConfigTest extends \PHPUnit_Framework_TestCase
$this->config->removeSparklineMetric('nb_unique_visitors');
$this->assertSame(array(
- array('columns' => 'nb_visits', 'order' => null),
- array('columns' => array('nb_downloads', 'nb_outlinks'), 'order' => null),
+ array('columns' => 'nb_visits', 'order' => null, 'graphParams' => null),
+ array('columns' => array('nb_downloads', 'nb_outlinks'), 'order' => null, 'graphParams' => null),
), $this->config->getSparklineMetrics());
}
@@ -74,8 +74,8 @@ class SparklinesConfigTest extends \PHPUnit_Framework_TestCase
$this->config->removeSparklineMetric(array('nb_downloads', 'nb_outlinks'));
$this->assertSame(array(
- array('columns' => 'nb_visits', 'order' => null),
- array('columns' => 'nb_unique_visitors', 'order' => 99),
+ array('columns' => 'nb_visits', 'order' => null, 'graphParams' => null),
+ array('columns' => 'nb_unique_visitors', 'order' => 99, 'graphParams' => null),
), $this->config->getSparklineMetrics());
}
@@ -86,9 +86,9 @@ class SparklinesConfigTest extends \PHPUnit_Framework_TestCase
$this->config->replaceSparklineMetric('nb_unique_visitors', '');
$this->assertSame(array(
- array('columns' => 'nb_visits', 'order' => null),
- array('columns' => '', 'order' => 99),
- array('columns' => array('nb_downloads', 'nb_outlinks'), 'order' => null),
+ array('columns' => 'nb_visits', 'order' => null, 'graphParams' => null),
+ array('columns' => '', 'order' => 99, 'graphParams' => null),
+ array('columns' => array('nb_downloads', 'nb_outlinks'), 'order' => null, 'graphParams' => null),
), $this->config->getSparklineMetrics());
}
@@ -99,9 +99,9 @@ class SparklinesConfigTest extends \PHPUnit_Framework_TestCase
$this->config->replaceSparklineMetric(array('nb_downloads', 'nb_outlinks'), '');
$this->assertSame(array(
- array('columns' => 'nb_visits', 'order' => null),
- array('columns' => 'nb_unique_visitors', 'order' => 99),
- array('columns' => '', 'order' => null),
+ array('columns' => 'nb_visits', 'order' => null, 'graphParams' => null),
+ array('columns' => 'nb_unique_visitors', 'order' => 99, 'graphParams' => null),
+ array('columns' => '', 'order' => null, 'graphParams' => null),
), $this->config->getSparklineMetrics());
}
@@ -113,10 +113,10 @@ class SparklinesConfigTest extends \PHPUnit_Framework_TestCase
$this->config->addPlaceholder($order = 3);
$this->assertSame(array(
- array('url' => '', 'metrics' => array(), 'order' => 3),
- array('url' => '', 'metrics' => array(), 'order' => 10),
- array('url' => '', 'metrics' => array(), 'order' => 999),
- array('url' => '', 'metrics' => array(), 'order' => 1001),
+ 'placeholder3' => [['url' => '', 'metrics' => array(), 'order' => 3, 'group' => 'placeholder3']],
+ 'placeholder1' => [['url' => '', 'metrics' => array(), 'order' => 10, 'group' => 'placeholder1']],
+ 'placeholder0' => [['url' => '', 'metrics' => array(), 'order' => 999, 'group' => 'placeholder0']],
+ 'placeholder2' => [['url' => '', 'metrics' => array(), 'order' => 1001, 'group' => 'placeholder2']],
), $this->config->getSortedSparklines());
}
@@ -126,5 +126,4 @@ class SparklinesConfigTest extends \PHPUnit_Framework_TestCase
$this->config->addSparklineMetric('nb_unique_visitors', 99);
$this->config->addSparklineMetric(array('nb_downloads', 'nb_outlinks'));
}
-
}