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:
authorBeezyT <timo@ezdesign.de>2011-11-04 18:03:56 +0400
committerBeezyT <timo@ezdesign.de>2011-11-04 18:03:56 +0400
commitf7a2ffc3daae339b3d7bdcb6fb477cc8d3ff5600 (patch)
treead34e24d200f54e94f15edeb8d485ba624a49608
parent460264a9003701acce15251c52867a63fa021a7a (diff)
refs #1820 metrics picker, refs #1454 using Api.get for cross-plugin evolution graphs
git-svn-id: http://dev.piwik.org/svn/trunk@5408 59fd770c-687e-43c8-a1e3-f5a4ff64c105
-rw-r--r--core/Controller.php97
-rw-r--r--core/ViewDataTable/GenerateGraphData/ChartEvolution.php68
-rw-r--r--core/Visualization/Chart.php98
-rw-r--r--core/Visualization/Chart/Evolution.php8
-rw-r--r--lang/de.php1
-rw-r--r--lang/en.php1
-rw-r--r--libs/jqplot/plugins/jqplot.canvasAxisTickRenderer.js3
-rw-r--r--plugins/API/API.php94
-rw-r--r--plugins/Actions/API.php33
-rw-r--r--plugins/Actions/Actions.php22
-rw-r--r--plugins/CoreHome/templates/graph.tpl5
-rw-r--r--plugins/CoreHome/templates/jqplot.css55
-rw-r--r--plugins/CoreHome/templates/jqplot.js273
-rw-r--r--plugins/CoreHome/templates/sparkline.js9
-rw-r--r--plugins/VisitsSummary/API.php88
-rw-r--r--plugins/VisitsSummary/Controller.php30
-rw-r--r--plugins/VisitsSummary/VisitsSummary.php6
-rw-r--r--tests/integration/Main.test.php9
-rw-r--r--tests/integration/expected/test_ecommerceOrderWithItems_API_get__API.get_day.xml7
-rw-r--r--tests/integration/expected/test_ecommerceOrderWithItems_API_get__API.get_week.xml7
-rw-r--r--themes/default/images/chart_line_edit.pngbin0 -> 718 bytes
21 files changed, 736 insertions, 178 deletions
diff --git a/core/Controller.php b/core/Controller.php
index 28880437af..dbc7e84114 100644
--- a/core/Controller.php
+++ b/core/Controller.php
@@ -178,6 +178,103 @@ abstract class Piwik_Controller
return $view;
}
+
+ /**
+ * This method is similar to self::getLastUnitGraph. It works with API.get to combine metrics
+ * of different *.get reports. The returned ViewDataTable is configured with column
+ * translations and selectable metrics.
+ *
+ * @param string $currentModuleName
+ * @param string $currentControllerAction
+ * @param array $columnsToDisplay in a format like ["VisitsSummary.nb_visits","Actions.nb_uniq_pageviews"]
+ * @param array $selectableColumns in the same format as the parameter above
+ * @param string $reportDocumentation the documentation to set on the report
+ * @return Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution
+ */
+ protected function getLastUnitGraphAcrossPlugins($currentModuleName, $currentControllerAction,
+ $columnsToDisplay, $selectableColumns=array(), $reportDocumentation=false)
+ {
+ // back up and manipulate the columns parameter
+ $backupColumns = false;
+ if (isset($_GET['columns']))
+ {
+ $backupColumns = $_GET['columns'];
+ }
+
+ $_GET['columns'] = implode(',', $columnsToDisplay);
+
+ // split the requested columns between the plugins
+ $columnsPerPlugin = array();
+ foreach (array_merge($columnsToDisplay, $selectableColumns) as $column)
+ {
+ @list($plugin, $col) = explode('.', $column);
+ $columnsPerPlugin[$plugin][] = $col;
+ }
+
+ // load meta data for the requested plugins
+ $idSite = Piwik_Common::getRequestVar('idSite');
+ $period = Piwik_Common::getRequestVar('period');
+ $date = Piwik_Common::getRequestVar('date');
+ $meta = array();
+ foreach ($columnsPerPlugin as $plugin => $columns)
+ {
+ $meta[$plugin] = Piwik_API_API::getInstance()->getMetadata(
+ $idSite, $plugin, 'get', array(), false, $period, $date);
+ $meta[$plugin] = &$meta[$plugin][0];
+ }
+
+ // handle wildcards like VisitsSummary.* in $selectableColumns
+ foreach ($selectableColumns as $i => $column)
+ {
+ if (substr($column, -2) == '.*')
+ {
+ $plugin = substr($column, 0, -2);
+ unset($selectableColumns[$i]);
+ $columnsPerPlugin[$plugin] = array();
+ foreach ($meta[$plugin]['metrics'] as $column => $translation)
+ {
+ $selectableColumns[] = $plugin.'.'.$column;
+ $columnsPerPlugin[$plugin][] = $column;
+ }
+ }
+ }
+
+ // get metrics translations from meta data
+ $translations = array();
+ foreach ($columnsPerPlugin as $plugin => $columns)
+ {
+ foreach ($columns as $column)
+ {
+ $translations[$plugin.'.'.$column] = isset($meta[$plugin]['metrics'][$column]) ?
+ $meta[$plugin]['metrics'][$column] : $column;
+ }
+ }
+
+ // initialize the graph and load the data
+ $view = $this->getLastUnitGraph($currentModuleName, $currentControllerAction, 'API.get');
+ $view->setColumnsToDisplay($columnsToDisplay);
+ $view->setSelectableColumns($selectableColumns);
+ $view->setColumnsTranslations($translations);
+
+ if ($reportDocumentation)
+ {
+ $view->setReportDocumentation($reportDocumentation);
+ }
+
+ $view->main();
+
+ // restore the columns parameter
+ if ($backupColumns !== false)
+ {
+ $_GET['columns'] = $backupColumns;
+ }
+ else
+ {
+ unset($_GET['columns']);
+ }
+
+ return $view;
+ }
/**
* Returns the array of new processed parameters once the parameters are applied.
diff --git a/core/ViewDataTable/GenerateGraphData/ChartEvolution.php b/core/ViewDataTable/GenerateGraphData/ChartEvolution.php
index 368f22d619..9cdb62318e 100644
--- a/core/ViewDataTable/GenerateGraphData/ChartEvolution.php
+++ b/core/ViewDataTable/GenerateGraphData/ChartEvolution.php
@@ -18,6 +18,9 @@
*/
class Piwik_ViewDataTable_GenerateGraphData_ChartEvolution extends Piwik_ViewDataTable_GenerateGraphData
{
+
+ protected $selectableColumns = array();
+
protected function checkStandardDataTable()
{
// DataTable_Array and DataTable allowed for the evolution chart
@@ -38,23 +41,38 @@ class Piwik_ViewDataTable_GenerateGraphData_ChartEvolution extends Piwik_ViewDat
$this->view = new Piwik_Visualization_Chart_Evolution();
}
+ /**
+ * Sets the columns that can be added/removed by the user
+ * This is done on data level (not html level) because the columns might change after reloading via sparklines
+ * @param array $columnsNames Array of column names eg. array('nb_visits','nb_hits')
+ */
+ public function setSelectableColumns($columnsNames)
+ {
+ $this->selectableColumns = $columnsNames;
+ }
+
protected function guessUnitFromRequestedColumnNames($requestedColumnNames, $idSite)
{
$nameToUnit = array(
'_rate' => '%',
'_revenue' => Piwik::getCurrency($idSite),
+ '_time_' => 's'
);
+
+ $units = array();
foreach($requestedColumnNames as $columnName)
{
+ $units[$columnName] = false;
foreach($nameToUnit as $pattern => $type)
{
if(strpos($columnName, $pattern) !== false)
{
- return $type;
+ $units[$columnName] = $type;
+ break;
}
}
}
- return false;
+ return $units;
}
protected function loadDataTableFromAPI()
@@ -101,7 +119,20 @@ class Piwik_ViewDataTable_GenerateGraphData_ChartEvolution extends Piwik_ViewDat
}
$requestedColumnNames = $this->getColumnsToDisplay();
- $yAxisLabelToValue = array();
+
+ // derive units from column names
+ $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
+ $units = $this->guessUnitFromRequestedColumnNames($requestedColumnNames, $idSite);
+ if(!empty($this->yAxisUnit))
+ {
+ // force unit to the value set via $this->setAxisYUnit()
+ foreach ($units as &$unit)
+ {
+ $unit = $this->yAxisUnit;
+ }
+ }
+
+ $yAxisLabelToUnit = array();
foreach($this->dataTable->getArray() as $idDataTable => $dataTable)
{
foreach($dataTable->getRows() as $row)
@@ -120,9 +151,11 @@ class Piwik_ViewDataTable_GenerateGraphData_ChartEvolution extends Piwik_ViewDat
// eg. "Visits"
$yAxisLabel = $metricLabel;
}
+
if(($columnValue = $row->getColumn($requestedColumnName)) !== false)
{
$yAxisLabelToValue[$yAxisLabel][$idDataTable] = $columnValue;
+ $yAxisLabelToUnit[$yAxisLabel] = $units[$requestedColumnName];
}
}
}
@@ -130,12 +163,10 @@ class Piwik_ViewDataTable_GenerateGraphData_ChartEvolution extends Piwik_ViewDat
// make sure all column values are set to at least zero (no gap in the graph)
$yAxisLabelToValueCleaned = array();
- $yAxisLabels = array();
foreach($uniqueIdsDataTable as $uniqueIdDataTable)
{
foreach($yAxisLabelToValue as $yAxisLabel => $idDataTableToColumnValue)
{
- $yAxisLabels[$yAxisLabel] = $yAxisLabel;
if(isset($idDataTableToColumnValue[$uniqueIdDataTable]))
{
$columnValue = $idDataTableToColumnValue[$uniqueIdDataTable];
@@ -147,18 +178,10 @@ class Piwik_ViewDataTable_GenerateGraphData_ChartEvolution extends Piwik_ViewDat
$yAxisLabelToValueCleaned[$yAxisLabel][] = $columnValue;
}
}
- $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
-
- $unit = $this->yAxisUnit;
- if(empty($unit))
- {
- $unit = $this->guessUnitFromRequestedColumnNames($requestedColumnNames, $idSite);
- }
$this->view->setAxisXLabels($xLabels);
$this->view->setAxisYValues($yAxisLabelToValueCleaned);
- $this->view->setAxisYLabels($yAxisLabels);
- $this->view->setAxisYUnit($unit);
+ $this->view->setAxisYUnits($yAxisLabelToUnit);
$countGraphElements = $this->dataTable->getRowsCount();
$firstDatatable = reset($this->dataTable->metadata);
@@ -206,6 +229,23 @@ class Piwik_ViewDataTable_GenerateGraphData_ChartEvolution extends Piwik_ViewDat
}
$this->view->setAxisXOnClick($axisXOnClick);
}
+
+
+ // Build the final configuration for the series picker
+ if (count($this->selectableColumns))
+ {
+ $columnsToDisplay = $this->getColumnsToDisplay();
+ $selectableColumns = array();
+ foreach ($this->selectableColumns as $column)
+ {
+ $selectableColumns[] = array(
+ 'column' => $column,
+ 'translation' => $this->getColumnTranslation($column),
+ 'displayed' => in_array($column, $columnsToDisplay)
+ );
+ }
+ $this->view->setSelectabelColumns($selectableColumns);
+ }
}
/**
diff --git a/core/Visualization/Chart.php b/core/Visualization/Chart.php
index 964bedd22d..cf2c0ffb04 100644
--- a/core/Visualization/Chart.php
+++ b/core/Visualization/Chart.php
@@ -26,6 +26,7 @@ abstract class Piwik_Visualization_Chart implements Piwik_iView
protected $axes = array();
protected $tooltip = array();
protected $seriesColors = array('#000000');
+ protected $seriesPicker = array();
// other attributes (not directly used for jqplot)
protected $maxValue;
@@ -64,11 +65,52 @@ abstract class Piwik_Visualization_Chart implements Piwik_iView
public function setAxisYUnit($yUnit)
{
- if (!empty($yUnit))
+ $yUnits = array();
+ for ($i = 0; $i < count($this->data); $i++)
{
- $this->yUnit = $yUnit;
- $this->axes['yaxis']['tickOptions']['formatString'] = '%s'.$yUnit;
- $this->tooltip['yUnit'] = $yUnit;
+ $yUnits[] = $yUnit;
+ }
+ $this->setAxisYUnits($yUnits);
+ }
+
+ public function setAxisYUnits($yUnits)
+ {
+ // generate an axis config for each unit
+ $axesIds = array();
+ // associate each series with the appropriate axis
+ $seriesAxes = array();
+ // units for tooltips
+ $seriesUnits = array();
+ foreach ($yUnits as $unit)
+ {
+ // handle axes ids: first y[]axis, then y[2]axis, y[3]axis...
+ $nextAxisId = empty($axesIds) ? '' : count($axesIds) + 1;
+
+ $unit = $unit ? $unit : '';
+ if (!isset($axesIds[$unit]))
+ {
+ $axesIds[$unit] = array('id' => $nextAxisId, 'unit' => $unit);
+ $seriesAxes[] = 'y'.$nextAxisId.'axis';
+ }
+ else
+ {
+ // reuse existing axis
+ $seriesAxes[] = 'y'.$axesIds[$unit]['id'].'axis';
+ }
+ $seriesUnits[] = $unit;
+ }
+
+ // generate jqplot axes config
+ foreach ($axesIds as $axis) {
+ $axisKey = 'y'.$axis['id'].'axis';
+ $this->axes[$axisKey]['tickOptions']['formatString'] = '%s'.$axis['unit'];
+ }
+
+ $this->tooltip['yUnits'] = $seriesUnits;
+
+ // add axis config to series
+ foreach ($seriesAxes as $i => $axisName) {
+ $this->series[$i]['yaxis'] = $axisName;
}
}
@@ -93,33 +135,12 @@ abstract class Piwik_Visualization_Chart implements Piwik_iView
{
$this->xSteps = $steps;
}
-
- public function getMaxValue()
+
+ public function setSelectabelColumns($selectableColumns)
{
- if (count($this->data) == 0)
- {
- return 0;
- }
-
- $maxCrossDataSets = 0;
- foreach ($this->data as &$data)
- {
- $maxValue = max($data);
- if($maxValue > $maxCrossDataSets)
- {
- $maxCrossDataSets = $maxValue;
- }
- }
-
- $maxCrossDataSets += round($maxCrossDataSets * .02);
-
- if ($maxCrossDataSets > 10)
- {
- $maxCrossDataSets = $maxCrossDataSets + 10 - $maxCrossDataSets % 10;
- }
- return $maxCrossDataSets;
+ $this->seriesPicker['selectableColumns'] = $selectableColumns;
}
-
+
public function render()
{
Piwik::overrideCacheControlHeaders();
@@ -131,7 +152,8 @@ abstract class Piwik_Visualization_Chart implements Piwik_iView
'seriesColors' => &$this->seriesColors
),
'data' => &$this->data,
- 'tooltip' => $this->tooltip
+ 'tooltip' => &$this->tooltip,
+ 'seriesPicker' => &$this->seriesPicker
);
return json_encode($data);
@@ -139,12 +161,6 @@ abstract class Piwik_Visualization_Chart implements Piwik_iView
public function customizeChartProperties()
{
- $this->maxValue = $this->getMaxValue();
- if ($this->maxValue == 0)
- {
- $this->maxValue = 1;
- }
-
// x axis labels with steps
if (isset($this->axes['xaxis']['ticks']))
{
@@ -157,16 +173,6 @@ abstract class Piwik_Visualization_Chart implements Piwik_iView
}
}
}
-
- // y axis labels
- $ticks = array();
- $numberOfTicks = 2;
- $tickDistance = ceil($this->maxValue / $numberOfTicks);
- for ($i = 0; $i <= $numberOfTicks; $i++)
- {
- $ticks[] = $i * $tickDistance;
- }
- $this->axes['yaxis']['ticks'] = &$ticks;
}
}
diff --git a/core/Visualization/Chart/Evolution.php b/core/Visualization/Chart/Evolution.php
index 3251171b7e..01a811f439 100644
--- a/core/Visualization/Chart/Evolution.php
+++ b/core/Visualization/Chart/Evolution.php
@@ -22,7 +22,7 @@ class Piwik_Visualization_Chart_Evolution extends Piwik_Visualization_Chart
protected $seriesColors = array('#5170AE','#F29007', '#CC3399', '#9933CC', '#80a033',
'#246AD2', '#FD16EA', '#49C100');
- function customizeChartProperties()
+ public function customizeChartProperties()
{
parent::customizeChartProperties();
@@ -34,4 +34,10 @@ class Piwik_Visualization_Chart_Evolution extends Piwik_Visualization_Chart
$this->axes['yaxis']['ticks'] = array(0, 50, 100);
}
}
+
+ public function setSelectabelColumns($selectableColumns)
+ {
+ $this->seriesPicker['selectableColumns'] = $selectableColumns;
+ }
+
}
diff --git a/lang/de.php b/lang/de.php
index e0a5e1c573..dfc83eacfd 100644
--- a/lang/de.php
+++ b/lang/de.php
@@ -41,6 +41,7 @@ $translations = array(
'General_Description' => 'Beschreibung',
'General_Done' => 'Erledigt',
'General_PoweredBy' => 'Powered by',
+ 'General_MetricsToPlot' => 'Metriken anzeigen',
'General_Name' => 'Name',
'General_Value' => 'Wert',
'General_Total' => 'Gesamt',
diff --git a/lang/en.php b/lang/en.php
index a04c162acb..28fc14943b 100644
--- a/lang/en.php
+++ b/lang/en.php
@@ -41,6 +41,7 @@ $translations = array(
'General_Description' => 'Description',
'General_Done' => 'Done',
'General_PoweredBy' => 'Powered by',
+ 'General_MetricsToPlot' => 'Metrics to plot',
'General_Name' => 'Name',
'General_Value' => 'Value',
'General_Total' => 'Total',
diff --git a/libs/jqplot/plugins/jqplot.canvasAxisTickRenderer.js b/libs/jqplot/plugins/jqplot.canvasAxisTickRenderer.js
index 81c6756eeb..2e59e87925 100644
--- a/libs/jqplot/plugins/jqplot.canvasAxisTickRenderer.js
+++ b/libs/jqplot/plugins/jqplot.canvasAxisTickRenderer.js
@@ -149,6 +149,9 @@
this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts);
}
};
+
+ $.jqplot.CanvasAxisTickRenderer.prototype = new $.jqplot.ElemContainer();
+ $.jqplot.CanvasAxisTickRenderer.prototype.constructor = $.jqplot.CanvasAxisTickRenderer;
$.jqplot.CanvasAxisTickRenderer.prototype.init = function(options) {
$.extend(true, this, options);
diff --git a/plugins/API/API.php b/plugins/API/API.php
index 5e4350128d..4877adb4b3 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -765,4 +765,98 @@ class Piwik_API_API
? (@$a['order'] < @$b['order'] ? -1 : 1)
: $category;
}
+
+
+ /**
+ * Get a combined report of the *.get API methods.
+ * $columns has a format like "VisitsSummary.nb_visits,Actions.nb_uniq_pageviews".
+ * In the example above, the metric nb_visits from VisitsSummary.get and the metric
+ * nb_uniq_pageviews from Actions.get will be returned.
+ */
+ public function get( $idSite, $period, $date, $segment = false, $columns = false)
+ {
+ // get the columns that are requested per plugin
+ $columns = Piwik::getArrayFromApiParameter($columns);
+ $columnsByPlugin = array();
+ $columnNameMap = array();
+ foreach ($columns as $column) {
+ list($plugin, $metric) = explode('.', $column);
+ $columnsByPlugin[$plugin][] = $metric;
+ $columnNameMap[$plugin][$metric] = $plugin.'.'.$metric;
+ }
+
+ $mergedDataTable = false;
+ $params = compact('idSite', 'period', 'date', 'segment');
+ foreach ($columnsByPlugin as $plugin => $columns)
+ {
+ // load the data
+ $className = 'Piwik_'.$plugin.'_API';
+ $params['columns'] = implode(',', $columns);
+ $dataTable = Piwik_API_Proxy::getInstance()->call($className, 'get', $params);
+
+ // make sure the table has all columns
+ $array = ($dataTable instanceof Piwik_DataTable_Array ? $dataTable->getArray() : array($dataTable));
+ foreach ($array as $table)
+ {
+ $firstRow = $table->getFirstRow();
+ if (!$firstRow)
+ {
+ $firstRow = new Piwik_DataTable_Row;
+ $table->addRow($firstRow);
+ }
+ foreach ($columns as $column)
+ {
+ if ($firstRow->getColumn($column) === false)
+ {
+ $firstRow->setColumn($column, 0);
+ }
+
+ }
+ }
+
+ // prefix columns with plugin name
+ $dataTable->filter('ReplaceColumnNames', array($columnNameMap[$plugin]));
+
+ // merge reports
+ if ($mergedDataTable === false)
+ {
+ $mergedDataTable = $dataTable;
+ }
+ else
+ {
+ $this->mergeDataTables($mergedDataTable, $dataTable);
+ }
+ }
+
+ return $mergedDataTable;
+ }
+
+
+ /**
+ * Merge the columns of two data tables.
+ * Manipulates the first table.
+ */
+ private function mergeDataTables($table1, $table2)
+ {
+ // handle table arrays
+ if ($table1 instanceof Piwik_DataTable_Array && $table2 instanceof Piwik_DataTable_Array)
+ {
+ $subTables2 = $table2->getArray();
+ foreach ($table1->getArray() as $index => $subTable1)
+ {
+ $subTable2 = $subTables2[$index];
+ $this->mergeDataTables($subTable1, $subTable2);
+ }
+ return;
+ }
+
+ $firstRow1 = $table1->getFirstRow();
+ $firstRow2 = $table2->getFirstRow();
+ foreach ($firstRow2->getColumns() as $metric => $value)
+ {
+ $firstRow1->setColumn($metric, $value);
+ }
+ }
+
+
}
diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php
index 2e6a7eee0c..fa4d995025 100644
--- a/plugins/Actions/API.php
+++ b/plugins/Actions/API.php
@@ -56,9 +56,10 @@ class Piwik_Actions_API
* @param string $date
* @param string $segment
*/
- public function get( $idSite, $period, $date, $segment = false)
+ public function get( $idSite, $period, $date, $segment = false, $columns = false)
{
Piwik::checkUserHasViewAccess( $idSite );
+ $archive = Piwik_Archive::build( $idSite, $period, $date, $segment );
$metrics = array(
'Actions_nb_pageviews' => 'nb_pageviews',
@@ -69,9 +70,33 @@ class Piwik_Actions_API
'Actions_nb_uniq_outlinks' => 'nb_uniq_outlinks'
);
- $archive = Piwik_Archive::build( $idSite, $period, $date, $segment );
- $table = $archive->getDataTableFromNumeric(array_keys($metrics));
- $table->filter('ReplaceColumnNames', array($metrics));
+ // get requested columns
+ $columns = Piwik::getArrayFromApiParameter($columns);
+ if(!empty($columns))
+ {
+ // get the columns that are available and requested
+ $columns = array_intersect($columns, array_values($metrics));
+ $columns = array_values($columns); // make sure indexes are right
+ $nameReplace = array();
+ foreach ($columns as $i => $column)
+ {
+ $fullColumn = array_search($column, $metrics);
+ $columns[$i] = $fullColumn;
+ $nameReplace[$fullColumn] = $column;
+ }
+ }
+ else
+ {
+ // get all columns
+ $columns = array_keys($metrics);
+ $nameReplace = &$metrics;
+ }
+
+ $table = $archive->getDataTableFromNumeric($columns);
+
+ // replace labels (remove Actions_)
+ $table->filter('ReplaceColumnNames', array($nameReplace));
+
return $table;
}
diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php
index 209f441a2c..387053379d 100644
--- a/plugins/Actions/Actions.php
+++ b/plugins/Actions/Actions.php
@@ -137,6 +137,26 @@ class Piwik_Actions extends Piwik_Plugin
{
$reports = &$notification->getNotificationObject();
+ $reports[] = array(
+ 'category' => Piwik_Translate('Actions_Actions'),
+ 'name' => Piwik_Translate('Actions_Actions'),
+ 'module' => 'Actions',
+ 'action' => 'get',
+ 'metrics' => array(
+ 'nb_pageviews' => Piwik_Translate('General_ColumnPageviews'),
+ 'nb_uniq_pageviews' => Piwik_Translate('General_ColumnUniquePageviews'),
+ 'nb_downloads' => Piwik_Translate('Actions_ColumnDownloads'),
+ 'nb_uniq_downloads' => Piwik_Translate('Actions_ColumnUniqueDownloads'),
+ 'nb_outlinks' => Piwik_Translate('Actions_ColumnOutlinks'),
+ 'nb_uniq_outlinks' => Piwik_Translate('Actions_ColumnUniqueOutlinks')
+ ),
+ 'metricsDocumentation' => array(
+ // to do
+ ),
+ 'processedMetrics' => false,
+ 'order' => 1
+ );
+
$metrics = array(
'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'),
'nb_hits' => Piwik_Translate('General_ColumnPageviews'),
@@ -169,7 +189,7 @@ class Piwik_Actions extends Piwik_Plugin
'documentation' => Piwik_Translate('Actions_PagesReportDocumentation', '<br />')
.'<br />'.Piwik_Translate('General_UsePlusMinusIconsDocumentation'),
'processedMetrics' => false,
- 'order' => 1,
+ 'order' => 2,
);
$reports[] = array(
diff --git a/plugins/CoreHome/templates/graph.tpl b/plugins/CoreHome/templates/graph.tpl
index 1660d842c6..fbefb52675 100644
--- a/plugins/CoreHome/templates/graph.tpl
+++ b/plugins/CoreHome/templates/graph.tpl
@@ -14,11 +14,12 @@
<script type="text/javascript">
{literal} window.setTimeout(function() { {/literal}
- var plot = new JQPlot({$data});
+ var plot = new JQPlot({$data}, '{$properties.uniqueId}');
plot.render('{$graphType}', '{$chartDivId}', {literal} { {/literal}
noData: '{'General_NoDataForGraph'|translate|escape:'javascript'}',
exportTitle: '{'General_ExportAsImage_js'|translate|escape:'javascript'}',
- exportText: '{'General_SaveImageOnYourComputer_js'|translate|escape:'javascript'}'
+ exportText: '{'General_SaveImageOnYourComputer_js'|translate|escape:'javascript'}',
+ metricsToPlot: '{'General_MetricsToPlot'|translate|escape:'javascript'}'
{literal} }); {/literal}
{literal} }, 5); {/literal}
</script>
diff --git a/plugins/CoreHome/templates/jqplot.css b/plugins/CoreHome/templates/jqplot.css
index e7c5e7b8c8..8dbab8d8b0 100644
--- a/plugins/CoreHome/templates/jqplot.css
+++ b/plugins/CoreHome/templates/jqplot.css
@@ -51,6 +51,11 @@
margin-right: 10px;
}
+.jqplot-y2axis,
+.jqplot-y3axis {
+ margin-left: 10px;
+}
+
.jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick {
position: absolute;
}
@@ -92,4 +97,54 @@
left: 0px;
padding-bottom: 0.5em;
font-size: 1.2em;
+}
+
+.jqplot-seriespicker {
+ display: block;
+ position: absolute;
+ z-index: 9;
+ width: 24px;
+ height: 16px;
+ margin-top: 3px;
+ background: url(../../../themes/default/images/chart_line_edit.png) no-repeat center center;
+ overflow: hidden;
+ text-indent: -999px;
+}
+
+.jqplock-seriespicker-popover {
+ display: block;
+ position: absolute;
+ z-index: 8;
+ margin-top: -2px;
+ background: #f7f7f7;
+ font-size: 11px;
+ font-weight: normal;
+ border: 1px solid #e4e5e4;
+ padding: 6px 9px;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ -moz-box-shadow: 1px 1px 2px #666;
+ -webkit-box-shadow: 1px 1px 2px #666;
+ box-shadow: 1px 1px 2px #666;
+}
+
+.jqplock-seriespicker-popover p {
+ margin: 0;
+ padding: 0 4px 0 0;
+}
+
+.jqplock-seriespicker-popover p.headline {
+ font-weight: bold;
+ font-size: 12px;
+ padding: 0 0 6px 22px;
+ color: #7E7363;
+}
+
+.jqplock-seriespicker-popover.alignright p.headline {
+ padding: 0 22px 6px 0;
+}
+
+.jqplock-seriespicker-popover input.select {
+ margin-right: 8px;
} \ No newline at end of file
diff --git a/plugins/CoreHome/templates/jqplot.js b/plugins/CoreHome/templates/jqplot.js
index 5a0b6fcb8e..ccc49cebdc 100644
--- a/plugins/CoreHome/templates/jqplot.js
+++ b/plugins/CoreHome/templates/jqplot.js
@@ -14,18 +14,20 @@ var jqPlotTooltip = false;
* Constructor function
* @param the data that would be passed to open flash chart
*/
-function JQPlot(data) {
- this.init(data);
+function JQPlot(data, dataTableId) {
+ this.init(data, dataTableId);
}
JQPlot.prototype = {
/** Generic init function */
- init: function(data) {
+ init: function(data, dataTableId) {
+ this.dataTableId = dataTableId;
this.originalData = data;
this.params = data.params;
this.data = data.data;
this.tooltip = data.tooltip;
+ this.seriesPicker = data.seriesPicker;
this.params.grid = {
drawGridLines: false,
@@ -64,7 +66,7 @@ JQPlot.prototype = {
// preapare the appropriate chart type
switch (type) {
case 'evolution':
- this.prepareEvolutionChart(targetDivId);
+ this.prepareEvolutionChart(targetDivId, lang);
break;
case 'bar':
this.prepareBarChart();
@@ -80,6 +82,7 @@ JQPlot.prototype = {
// this has be bound before the check for an empty graph.
// otherwise clicking on sparklines won't work anymore after an empty
// report has been displayed.
+ var self = this;
var target = $('#' + targetDivId)
.on('replot', function(e, data) {
target.trigger('piwikDestroyPlot');
@@ -90,7 +93,31 @@ JQPlot.prototype = {
this.innerHTML = '';
}
- (new JQPlot(data)).render(type, targetDivId, lang);
+ (new JQPlot(data, self.dataTableId)).render(type, targetDivId, lang);
+ });
+
+ // show loading
+ target.bind('showLoading', function() {
+ var loading = $(document.createElement('div')).addClass('jqplot-loading');
+ loading.css({
+ width: target.innerWidth()+'px',
+ height: target.innerHeight()+'px',
+ opacity: 0
+ });
+ target.prepend(loading);
+ loading.css({opacity: .7});
+ });
+
+ // change columns
+ target.bind('changeColumns', function(e, columns) {
+ target.trigger('showLoading');
+ if (typeof columns == 'string') {
+ columns = columns.split(',');
+ }
+ var dataTable = dataTables[self.dataTableId];
+ dataTable.param.columns = columns.join(',');
+ dataTable.param.viewDataTable = 'graphEvolution';
+ dataTable.reloadAjaxDataTable(false);
});
// this case happens when there is no data for a line chart
@@ -134,7 +161,7 @@ JQPlot.prototype = {
if (width > 0 && Math.abs(plotWidth - width) >= 5) {
plotWidth = width;
target.trigger('piwikDestroyPlot');
- (new JQPlot(self.originalData))
+ (new JQPlot(self.originalData, self.dataTableId))
.render(type, targetDivId, lang);
}
};
@@ -235,7 +262,9 @@ JQPlot.prototype = {
// EVOLTION CHART
// ------------------------------------------------------------
- prepareEvolutionChart: function(targetDivId) {
+ prepareEvolutionChart: function(targetDivId, lang) {
+ this.setYTicks();
+
this.params.axes.xaxis.pad = 1.0;
this.params.axes.xaxis.renderer = $.jqplot.CategoryAxisRenderer;
this.params.axes.xaxis.tickOptions = {
@@ -287,6 +316,13 @@ JQPlot.prototype = {
this.params.canvasLegend = {
show: true
};
+ this.params.seriesPicker = {
+ show: typeof this.seriesPicker.selectableColumns == 'object',
+ selectableColumns: this.seriesPicker.selectableColumns,
+ targetDivId: targetDivId,
+ dataTableId: this.dataTableId,
+ lang: lang
+ };
},
showEvolutionChartTooltip: function(i) {
@@ -299,7 +335,7 @@ JQPlot.prototype = {
var text = [];
for (var d = 0; d < this.data.length; d++) {
- var value = this.formatY(this.data[d][i]);
+ var value = this.formatY(this.data[d][i], d);
var series = this.params.series[d].label;
text.push('<b>' + value + '</b> ' + series);
}
@@ -343,7 +379,7 @@ JQPlot.prototype = {
},
showPieChartTooltip: function(i) {
- var value = this.formatY(this.data[0][i][1]);
+ var value = this.formatY(this.data[0][i][1], 0);
var series = this.params.series[0].label;
var percentage = this.tooltip.percentages[0][i];
@@ -358,6 +394,8 @@ JQPlot.prototype = {
// ------------------------------------------------------------
prepareBarChart: function() {
+ this.setYTicks();
+
this.params.seriesDefaults = {
renderer: $.jqplot.BarRenderer,
rendererOptions: {
@@ -386,7 +424,7 @@ JQPlot.prototype = {
},
showBarChartTooltip: function(i) {
- var value = this.formatY(this.data[0][i]);
+ var value = this.formatY(this.data[0][i], 0);
var series = this.params.series[0].label;
var percentage = '';
@@ -405,8 +443,53 @@ JQPlot.prototype = {
// HELPER METHODS
// ------------------------------------------------------------
+ /** Generate ticks in y direction */
+ setYTicks: function() {
+ // default axis
+ this.setYTicksForAxis('yaxis', this.params.axes.yaxis);
+ // other axes: y2axis, y3axis...
+ for (var i = 2; typeof this.params.axes['y'+i+'axis'] != 'undefined'; i++) {
+ this.setYTicksForAxis('y'+i+'axis', this.params.axes['y'+i+'axis']);
+ }
+ },
+
+ setYTicksForAxis: function(axisName, axis) {
+ // calculate maximum x value of all data sets
+ var maxCrossDataSets = 0;
+ for (var i = 0; i < this.data.length; i++) {
+ if (this.params.series[i].yaxis == axisName) {
+ maxValue = Math.max.apply(Math, this.data[i]);
+ if (maxValue > maxCrossDataSets) {
+ maxCrossDataSets = maxValue;
+ }
+ maxCrossDataSets = parseFloat(maxCrossDataSets);
+ }
+ }
+
+ // add little padding on top
+ maxCrossDataSets += Math.round(maxCrossDataSets * .03);
+
+ // round to the nearest multiple of ten
+ if (maxCrossDataSets > 15) {
+ maxCrossDataSets = maxCrossDataSets + 10 - maxCrossDataSets % 10;
+ }
+
+ if (maxCrossDataSets == 0) {
+ maxCrossDataSets = 1;
+ }
+
+ // calculate y-values for ticks
+ ticks = [];
+ numberOfTicks = 2;
+ tickDistance = Math.ceil(maxCrossDataSets / numberOfTicks);
+ for (var i = 0; i <= numberOfTicks; i++) {
+ ticks.push(i * tickDistance);
+ }
+ axis.ticks = ticks;
+ },
+
/** Get a formatted y values (with unit) */
- formatY: function(value) {
+ formatY: function(value, seriesIndex) {
var floatVal = parseFloat(value);
var intVal = parseInt(value, 10);
if (Math.abs(floatVal - intVal) >= 0.005) {
@@ -417,8 +500,8 @@ JQPlot.prototype = {
value = floatVal;
}
- if (typeof this.tooltip.yUnit != 'undefined') {
- value += this.tooltip.yUnit;
+ if (typeof this.tooltip.yUnits[seriesIndex] != 'undefined') {
+ value += this.tooltip.yUnits[seriesIndex];
}
return value;
@@ -671,8 +754,9 @@ JQPlot.prototype = {
}
// initialize legend canvas
- var padding = {top: 0, right: 0, bottom: 0, left: this._gridPadding.left};
+ var padding = {top: 0, right: this._gridPadding.right, bottom: 0, left: this._gridPadding.left};
var dimensions = {width: this._plotDimensions.width, height: this._gridPadding.top};
+ var width = this._plotDimensions.width - this._gridPadding.left - this._gridPadding.right;
legend.legendCanvas = new $.jqplot.GenericCanvas();
this.eventCanvas._elem.before(legend.legendCanvas.createElement(
@@ -700,10 +784,20 @@ JQPlot.prototype = {
ctx.fillRect(x, 10, 10, 2);
x += 15;
+ var nextX = x + ctx.measureText(label).width + 20;
+
+ if (nextX + 70 > width) {
+ ctx.fillText("[...]", x, 15);
+ x += ctx.measureText("[...]").width + 20;
+ break;
+ }
+
ctx.fillText(label, x, 15);
- x += ctx.measureText(label).width + 20;
+ x = nextX;
}
+ legend.width = x;
+
ctx.restore();
};
@@ -715,6 +809,155 @@ JQPlot.prototype = {
// ------------------------------------------------------------
+// SERIES PICKER
+// For line charts
+// ------------------------------------------------------------
+
+(function($) {
+
+ $.jqplot.SeriesPicker = function(options) {
+ // dom element
+ this.domElem = null;
+ // render the picker?
+ this.show = false;
+ // the columns that can be selected
+ this.selectableColumns = null;
+ // css id of the target div dom element
+ this.targetDivId = "";
+ // the id of the current data table (index for global dataTables)
+ this.dataTableId = "";
+ // language strings
+ this.lang = {};
+
+ $.extend(true, this, options);
+ };
+
+ $.jqplot.SeriesPicker.init = function(target, data, opts) {
+ // add plugin as an attribute to the plot
+ var options = opts || {};
+ this.plugins.seriesPicker = new $.jqplot.SeriesPicker(options.seriesPicker);
+ };
+
+ // render the link to add series
+ $.jqplot.SeriesPicker.postDraw = function() {
+ var plot = this;
+ var picker = plot.plugins.seriesPicker;
+
+ if (!picker.show) {
+ return;
+ }
+
+ // initialize dom element
+ picker.domElem = $(document.createElement('a'))
+ .addClass('jqplot-seriespicker')
+ .attr('href', '#').html('+')
+ .css('marginLeft', (this._gridPadding.left + plot.plugins.canvasLegend.width - 1) + 'px')
+ .hide();
+
+ plot.baseCanvas._elem.before(picker.domElem);
+
+ // show / hide dom element on legend hover
+ plot.plugins.canvasLegend.legendCanvas._elem.hover(function() {
+ picker.domElem.show();
+ }, function(e) {
+ if (!$(e.relatedTarget).is('.jqplot-seriespicker') && !picker.domElem.data('open')) {
+ picker.domElem.hide();
+ }
+ });
+ picker.domElem.mouseout(function() {
+ if (!picker.domElem.data('open')) {
+ picker.domElem.hide();
+ }
+ });
+
+ picker.domElem.click(function() {
+ if (!picker.domElem.data('open')) {
+ picker.domElem.data('open', true);
+ showPicker(picker, plot._width);
+ }
+ return false;
+ });
+ };
+
+ // show the series picker
+ function showPicker(picker, plotWidth) {
+ var pickerLink = picker.domElem;
+ var pickerPopover = $(document.createElement('div'))
+ .addClass('jqplock-seriespicker-popover').hide();
+
+ pickerLink.before(pickerPopover);
+
+ var manipulated = false;
+
+ pickerPopover.mouseleave(function(e) {
+ if (!$(e.relatedTarget).is('.jqplot-seriespicker')) {
+ // if metrics list has been manipulated, replot
+ if (manipulated) {
+ var columns = [];
+ pickerPopover.find('input:checked').each(function() {
+ columns.push($(this).attr('name'));
+ });
+ if (columns.length > 0) {
+ $('#'+picker.targetDivId).trigger('changeColumns', [columns]);
+ }
+ }
+
+ // hide popupover
+ pickerPopover.hide();
+ pickerLink.hide().data('open', false);
+ }
+ });
+
+ // headline
+ pickerPopover.append($(document.createElement('p'))
+ .addClass('headline').html(picker.lang.metricsToPlot));
+
+ // render the selectable items
+ for (var i = 0; i < picker.selectableColumns.length; i++) {
+ var column = picker.selectableColumns[i];
+
+ var checkbox = $(document.createElement('input')).attr('type', 'checkbox').addClass('select');
+ if (column.displayed) {
+ checkbox.attr('checked', 'checked');
+ }
+ checkbox.attr('name', column.column);
+
+ var span = $(document.createElement('p'));
+ span.append(checkbox).append(column.translation)
+ span.click(function(e) {
+ manipulated = true;
+ if (!$(e.target).is('input.select')) {
+ var hit = $(this).find('input.select:not(:checked)').attr('checked', 'checked').size();
+ if (hit == 0) {
+ $(this).find('input.select:checked').removeAttr('checked');
+ }
+ };
+ });
+
+ pickerPopover.append(span);
+ }
+
+ var neededSpace = pickerPopover.outerWidth() + 10;
+
+ // try to display popover to the right
+ var margin = (parseInt(pickerLink.css('marginLeft'), 10) - 4);
+ if (margin + neededSpace < plotWidth) {
+ pickerPopover.css('marginLeft', margin + 'px').show();
+ } else {
+ // display to the left
+ margin = margin - neededSpace + 40;
+ pickerPopover.addClass('alignright').css('marginLeft', margin + 'px').show();
+ }
+ }
+
+ $.jqplot.preInitHooks.push($.jqplot.SeriesPicker.init);
+ $.jqplot.postDrawHooks.push($.jqplot.SeriesPicker.postDraw);
+
+})(jQuery);
+
+
+
+// ------------------------------------------------------------
// PIE CHART LEGEND PLUGIN FOR JQPLOT
// Render legend inside the pie graph
// ------------------------------------------------------------
diff --git a/plugins/CoreHome/templates/sparkline.js b/plugins/CoreHome/templates/sparkline.js
index 7971c5bf18..342815d20c 100644
--- a/plugins/CoreHome/templates/sparkline.js
+++ b/plugins/CoreHome/templates/sparkline.js
@@ -26,14 +26,7 @@ function initializeSparklines () {
var idDataTable = graph.attr('graphId');
//get the main page graph and reload with new data
var chart = $('#'+idDataTable+"Chart");
- var loading = $(document.createElement('div')).addClass('jqplot-loading');
- loading.css({
- width: chart.innerWidth()+'px',
- height: chart.innerHeight()+'px',
- opacity: 0
- });
- chart.prepend(loading);
- loading.css({opacity: .7});
+ chart.trigger('showLoading');
$.get(url, {}, function(data) {
chart.trigger('replot', data);
}, 'json');
diff --git a/plugins/VisitsSummary/API.php b/plugins/VisitsSummary/API.php
index f4b2c8228c..1063c29e7b 100644
--- a/plugins/VisitsSummary/API.php
+++ b/plugins/VisitsSummary/API.php
@@ -35,28 +35,36 @@ class Piwik_VisitsSummary_API
{
Piwik::checkUserHasViewAccess( $idSite );
$archive = Piwik_Archive::build($idSite, $period, $date, $segment );
-
+
// array values are comma separated
$columns = Piwik::getArrayFromApiParameter($columns);
- $countColumnsRequested = count($columns);
+ $tempColumns = array();
- $allColumns = true;
$bounceRateRequested = $actionsPerVisitRequested = $averageVisitDurationRequested = false;
- if(!empty($columns))
+ if($subsetOfColumns = !empty($columns))
{
- $allColumns = false;
- if(($bounceRateRequested = array_search('bounce_rate', $columns)) !== false)
+ // make sure base metrics are there for processed metrics
+ if(false !== ($bounceRateRequested = array_search('bounce_rate', $columns)))
{
- $columns = array('nb_visits', 'bounce_count');
+ if (!in_array('nb_visits', $columns)) $tempColumns[] = 'nb_visits';
+ if (!in_array('bounce_count', $columns)) $tempColumns[] = 'bounce_count';
+ unset($columns[$bounceRateRequested]);
}
- elseif(($actionsPerVisitRequested = array_search('nb_actions_per_visit', $columns)) !== false)
+ if(false !== ($actionsPerVisitRequested = array_search('nb_actions_per_visit', $columns)))
{
- $columns = array('nb_actions', 'nb_visits');
+ if (!in_array('nb_actions', $columns)) $tempColumns[] = 'nb_actions';
+ if (!in_array('nb_visits', $columns)) $tempColumns[] = 'nb_visits';
+ unset($columns[$actionsPerVisitRequested]);
}
- elseif(($averageVisitDurationRequested = array_search('avg_time_on_site', $columns)) !== false)
+ if(false !== ($averageVisitDurationRequested = array_search('avg_time_on_site', $columns)))
{
- $columns = array('sum_visit_length', 'nb_visits');
+ if (!in_array('sum_visit_length', $columns)) $tempColumns[] = 'sum_visit_length';
+ if (!in_array('nb_visits', $columns)) $tempColumns[] = 'nb_visits';
+ unset($columns[$averageVisitDurationRequested]);
}
+
+ $tempColumns = array_unique($tempColumns);
+ $columns = array_merge($columns, $tempColumns);
}
else
{
@@ -77,7 +85,7 @@ class Piwik_VisitsSummary_API
// Force reindex from 0 to N otherwise the SQL bind will fail
$columns = array_values($columns);
}
-
+
$dataTable = $archive->getDataTableFromNumeric($columns);
// Process ratio metrics from base metrics, when requested
@@ -94,60 +102,10 @@ class Piwik_VisitsSummary_API
$dataTable->filter('ColumnCallbackAddColumnQuotient', array('avg_time_on_site', 'sum_visit_length', 'nb_visits', 0));
}
- // If only a computed metrics was requested, we delete other metrics
- // that we selected only to process this one metric
- if($countColumnsRequested == 1
- && ($bounceRateRequested || $actionsPerVisitRequested || $averageVisitDurationRequested)
- )
- {
- $dataTable->deleteColumns($columns);
- }
- return $dataTable;
- }
-
- /**
- * merge the columns of two data tables
- * used to add the action counts to the visits summary
- * manipulates the first table
- */
- private function mergeDataTables($table1, $table2, $allColumns=true, $columns=array())
- {
- // handle table arrays
- if ($table1 instanceof Piwik_DataTable_Array && $table2 instanceof Piwik_DataTable_Array)
- {
- $subTables2 = $table2->getArray();
- foreach ($table1->getArray() as $index => $subTable1)
- {
- $subTable2 = $subTables2[$index];
- $this->mergeDataTables($subTable1, $subTable2);
- }
- return;
- }
-
- $firstRow2 = $table2->getFirstRow();
- if (!$firstRow2)
- {
- // nothing to add
- return;
- }
+ // remove temp metrics that were used to compute processed metrics
+ $dataTable->deleteColumns($tempColumns);
- $firstRow1 = $table1->getFirstRow();
- if (!$firstRow1)
- {
- // first table has no row yet
- $firstRow1 = new Piwik_DataTable_Row;
- $table1->addRow($firstRow1);
- }
-
- foreach ($firstRow2->getColumns() as $metric => $value)
- {
- if (!$allColumns && !in_array($metric, $columns))
- {
- // only add the columns that have been requested
- continue;
- }
- $firstRow1->setColumn($metric, $value);
- }
+ return $dataTable;
}
protected function getNumeric( $idSite, $period, $date, $segment, $toFetch )
diff --git a/plugins/VisitsSummary/Controller.php b/plugins/VisitsSummary/Controller.php
index fb634addb0..8465be2e19 100644
--- a/plugins/VisitsSummary/Controller.php
+++ b/plugins/VisitsSummary/Controller.php
@@ -20,7 +20,8 @@ class Piwik_VisitsSummary_Controller extends Piwik_Controller
{
$view = Piwik_View::factory('index');
$this->setPeriodVariablesView($view);
- $view->graphEvolutionVisitsSummary = $this->getEvolutionGraph( true, array('nb_visits', 'nb_uniq_visitors') );
+ $view->graphEvolutionVisitsSummary = $this->getEvolutionGraph( true,
+ array('VisitsSummary.nb_visits', 'VisitsSummary.nb_uniq_visitors') );
$this->setSparklinesAndNumbers($view);
echo $view->render();
}
@@ -33,17 +34,13 @@ class Piwik_VisitsSummary_Controller extends Piwik_Controller
echo $view->render();
}
- public function getEvolutionGraph( $fetch = false, $columns = false)
+ public function getEvolutionGraph( $fetch = false, $columns = false )
{
- $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, "VisitsSummary.get");
if(empty($columns))
{
$columns = Piwik_Common::getRequestVar('columns');
+ $columns = Piwik::getArrayFromApiParameter($columns);
}
- $view->setColumnsToDisplay($columns);
-
- $meta = Piwik_API_API::getInstance()->getMetadata(false, 'VisitsSummary', 'get');
- $view->setColumnsTranslations($meta[0]['metrics']);
$doc = Piwik_Translate('VisitsSummary_VisitsSummaryDocumentation').'<br />'
. Piwik_Translate('General_BrokenDownReportDocumentation').'<br /><br />'
@@ -60,7 +57,8 @@ class Piwik_VisitsSummary_Controller extends Piwik_Controller
. '<b>'.Piwik_Translate('General_ColumnActionsPerVisit').':</b> '
. Piwik_Translate('General_ColumnActionsPerVisitDocumentation');
- $view->setReportDocumentation($doc);
+ $view = $this->getLastUnitGraphAcrossPlugins($this->pluginName, __FUNCTION__, $columns,
+ $selectableColumns = array('VisitsSummary.*', 'Actions.*'), $doc);
return $this->renderView($view, $fetch);
}
@@ -87,14 +85,14 @@ class Piwik_VisitsSummary_Controller extends Piwik_Controller
protected function setSparklinesAndNumbers($view)
{
- $view->urlSparklineNbVisits = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => $view->displayUniqueVisitors ? array('nb_visits', 'nb_uniq_visitors') : array('nb_visits')));
- $view->urlSparklineNbPageviews = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_pageviews', 'nb_uniq_pageviews')));
- $view->urlSparklineNbDownloads = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_downloads', 'nb_uniq_downloads')));
- $view->urlSparklineNbOutlinks = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_outlinks', 'nb_uniq_outlinks')));
- $view->urlSparklineAvgVisitDuration = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('avg_time_on_site')));
- $view->urlSparklineMaxActions = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('max_actions')));
- $view->urlSparklineActionsPerVisit = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_actions_per_visit')));
- $view->urlSparklineBounceRate = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('bounce_rate')));
+ $view->urlSparklineNbVisits = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => $view->displayUniqueVisitors ? array('VisitsSummary.nb_visits', 'VisitsSummary.nb_uniq_visitors') : array('VisitsSummary.nb_visits')));
+ $view->urlSparklineNbPageviews = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('Actions.nb_pageviews', 'Actions.nb_uniq_pageviews')));
+ $view->urlSparklineNbDownloads = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('Actions.nb_downloads', 'Actions.nb_uniq_downloads')));
+ $view->urlSparklineNbOutlinks = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('Actions.nb_outlinks', 'Actions.nb_uniq_outlinks')));
+ $view->urlSparklineAvgVisitDuration = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('VisitsSummary.avg_time_on_site')));
+ $view->urlSparklineMaxActions = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('VisitsSummary.max_actions')));
+ $view->urlSparklineActionsPerVisit = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('VisitsSummary.nb_actions_per_visit')));
+ $view->urlSparklineBounceRate = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('VisitsSummary.bounce_rate')));
$dataTableVisit = self::getVisitsSummary();
$dataRow = $dataTableVisit->getFirstRow();
diff --git a/plugins/VisitsSummary/VisitsSummary.php b/plugins/VisitsSummary/VisitsSummary.php
index 9d8cd04872..20303a27da 100644
--- a/plugins/VisitsSummary/VisitsSummary.php
+++ b/plugins/VisitsSummary/VisitsSummary.php
@@ -54,12 +54,6 @@ class Piwik_VisitsSummary extends Piwik_Plugin
'nb_actions',
'nb_actions_per_visit',
'bounce_rate',
- 'nb_pageviews' => Piwik_Translate('General_ColumnPageviews'),
- 'nb_uniq_pageviews' => Piwik_Translate('General_ColumnUniquePageviews'),
- 'nb_downloads' => Piwik_Translate('Actions_ColumnDownloads'),
- 'nb_uniq_downloads' => Piwik_Translate('Actions_ColumnUniqueDownloads'),
- 'nb_outlinks' => Piwik_Translate('Actions_ColumnOutlinks'),
- 'nb_uniq_outlinks' => Piwik_Translate('Actions_ColumnUniqueOutlinks'),
'avg_time_on_site' => Piwik_Translate('General_VisitDuration'),
'max_actions' => Piwik_Translate('General_ColumnMaxActions'),
// Used to process metrics, not displayed/used directly
diff --git a/tests/integration/Main.test.php b/tests/integration/Main.test.php
index f5fa1961dc..11f6f4d22d 100644
--- a/tests/integration/Main.test.php
+++ b/tests/integration/Main.test.php
@@ -266,6 +266,15 @@ class Test_Piwik_Integration_Main extends Test_Integration
$this->setApiToCall(array('Live.getLastVisitsDetails'));
$this->callGetApiCompareOutput(__FUNCTION__ . '_LiveEcommerceStatusOrdered', 'xml', $idSite, Piwik_Date::factory($dateTime)->addHour( 30.65 )->getDatetime(), $periods = array('day'));
+ // test API.get method
+ $this->setApiToCall(array('API.get'));
+ $this->callGetApiCompareOutput(__FUNCTION__ . '_API_get', 'xml', $idSite, $dateTime,
+ $periods = array('day', 'week'), $setDateLastN = false, $language = false,
+ $segment = false, $visitorId = false, $abandonedCarts = false, $idGoal = false,
+ $apiModule = false, $apiAction = false, $otherParams = array(
+ 'columns' => 'Actions.nb_pageviews,VisitsSummary.nb_visits,'
+ .'VisitsSummary.avg_time_on_site,Goals.nb_visits_converted'));
+
// Website2
$this->setApiToCall( array('Goals.get', 'Goals.getItemsSku', 'Goals.getItemsName', 'Goals.getItemsCategory' ) );
$this->callGetApiCompareOutput(__FUNCTION__ . '_Website2', 'xml', $idSite2, $dateTime, $periods = array('week'));
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_API_get__API.get_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems_API_get__API.get_day.xml
new file mode 100644
index 0000000000..3b3ea87298
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_API_get__API.get_day.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <Actions.nb_pageviews>13</Actions.nb_pageviews>
+ <VisitsSummary.nb_visits>3</VisitsSummary.nb_visits>
+ <VisitsSummary.avg_time_on_site>1801</VisitsSummary.avg_time_on_site>
+ <Goals.nb_visits_converted>2</Goals.nb_visits_converted>
+</result> \ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_API_get__API.get_week.xml b/tests/integration/expected/test_ecommerceOrderWithItems_API_get__API.get_week.xml
new file mode 100644
index 0000000000..fe3df7381e
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_API_get__API.get_week.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <Actions.nb_pageviews>16</Actions.nb_pageviews>
+ <VisitsSummary.nb_visits>5</VisitsSummary.nb_visits>
+ <VisitsSummary.avg_time_on_site>1369</VisitsSummary.avg_time_on_site>
+ <Goals.nb_visits_converted>4</Goals.nb_visits_converted>
+</result> \ No newline at end of file
diff --git a/themes/default/images/chart_line_edit.png b/themes/default/images/chart_line_edit.png
new file mode 100644
index 0000000000..9cf660733d
--- /dev/null
+++ b/themes/default/images/chart_line_edit.png
Binary files differ