diff options
author | BeezyT <timo@ezdesign.de> | 2011-11-04 18:03:56 +0400 |
---|---|---|
committer | BeezyT <timo@ezdesign.de> | 2011-11-04 18:03:56 +0400 |
commit | f7a2ffc3daae339b3d7bdcb6fb477cc8d3ff5600 (patch) | |
tree | ad34e24d200f54e94f15edeb8d485ba624a49608 | |
parent | 460264a9003701acce15251c52867a63fa021a7a (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
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 Binary files differnew file mode 100644 index 0000000000..9cf660733d --- /dev/null +++ b/themes/default/images/chart_line_edit.png |