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
path: root/core
diff options
context:
space:
mode:
authorThomas ZILLIOX <thomas@zilliox.me>2013-08-08 03:37:12 +0400
committerThomas ZILLIOX <thomas@zilliox.me>2013-08-08 03:37:12 +0400
commit6353a269ac30b5fe6b5da8ff7c97a2e14563661c (patch)
treef028ff54049c317e817e4c62ab5ae47458837de5 /core
parenta440086526162d35f54fbe1f5f167256bd636e89 (diff)
parent07c430fba0014336fce6071dd8a4c1fb9c65ef25 (diff)
Merge from piwik master
Diffstat (limited to 'core')
-rw-r--r--core/Archive.php13
-rw-r--r--core/Archive/DataCollection.php4
-rw-r--r--core/Archive/DataTableFactory.php41
-rw-r--r--core/ArchiveProcessor/Period.php2
-rw-r--r--core/AssetManager.php1
-rw-r--r--core/Controller.php9
-rw-r--r--core/DataTableVisualization.php202
-rw-r--r--core/FrontController.php2
-rw-r--r--core/JqplotDataGenerator.php205
-rw-r--r--core/JqplotDataGenerator/Evolution.php272
-rw-r--r--core/PluginsManager.php1
-rw-r--r--core/Tracker/Request.php14
-rw-r--r--core/Twig.php9
-rw-r--r--core/ViewDataTable.php447
-rw-r--r--core/ViewDataTable/Cloud.php81
-rw-r--r--core/ViewDataTable/Properties.php310
-rw-r--r--core/ViewDataTable/Sparkline.php9
-rw-r--r--core/ViewDataTable/VisualizationPropertiesProxy.php30
-rw-r--r--core/Visualization/Cloud.php161
-rw-r--r--core/Visualization/HtmlTable.php340
-rw-r--r--core/Visualization/JqplotGraph.php132
-rw-r--r--core/Visualization/JqplotGraph/Bar.php35
-rw-r--r--core/Visualization/JqplotGraph/Evolution.php149
-rw-r--r--core/Visualization/JqplotGraph/Pie.php36
24 files changed, 615 insertions, 1890 deletions
diff --git a/core/Archive.php b/core/Archive.php
index 6e060cc2da..2c53a8e665 100644
--- a/core/Archive.php
+++ b/core/Archive.php
@@ -251,15 +251,17 @@ class Archive
* Manager::getTable() function.
*
* @param string $name The name of the record to get.
- * @param int|string|null $idSubtable The subtable ID (if any) or self::ID_SUBTABLE_LOAD_ALL_SUBTABLES if requesting every datatable.
+ * @param int|string|null $idSubtable The subtable ID (if any) or self::ID_SUBTABLE_LOAD_ALL_SUBTABLES
+ * if requesting every datatable.
+ * @param int|null $depth The maximum number of subtable levels to load. If null, all levels are loaded.
* @param bool $addMetadataSubtableId Whether to add the DB subtable ID as metadata to each datatable,
* or not.
* @return DataTable
*/
- public function getDataTableExpanded($name, $idSubtable = null, $addMetadataSubtableId = true)
+ public function getDataTableExpanded($name, $idSubtable = null, $depth = null, $addMetadataSubtableId = true)
{
$data = $this->get($name, 'blob', self::ID_SUBTABLE_LOAD_ALL_SUBTABLES);
- return $data->getExpandedDataTable($this->getResultIndices(), $idSubtable, $addMetadataSubtableId);
+ return $data->getExpandedDataTable($this->getResultIndices(), $idSubtable, $depth, $addMetadataSubtableId);
}
/**
@@ -291,7 +293,8 @@ class Archive
* @param int|null $idSubtable
* @return DataTable|DataTable\Map
*/
- public static function getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded, $idSubtable = null)
+ public static function getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded,
+ $idSubtable = null, $depth = null)
{
Piwik::checkUserHasViewAccess($idSite);
$archive = Archive::build($idSite, $period, $date, $segment);
@@ -300,7 +303,7 @@ class Archive
}
if ($expanded) {
- $dataTable = $archive->getDataTableExpanded($name, $idSubtable);
+ $dataTable = $archive->getDataTableExpanded($name, $idSubtable, $depth);
} else {
$dataTable = $archive->getDataTable($name, $idSubtable);
}
diff --git a/core/Archive/DataCollection.php b/core/Archive/DataCollection.php
index 61499219d4..9cd96f3540 100644
--- a/core/Archive/DataCollection.php
+++ b/core/Archive/DataCollection.php
@@ -234,7 +234,7 @@ class DataCollection
* @throws Exception
* @return DataTable|DataTable\Map
*/
- public function getExpandedDataTable($resultIndices, $idSubTable = null, $addMetadataSubTableId = false)
+ public function getExpandedDataTable($resultIndices, $idSubTable = null, $depth = null, $addMetadataSubTableId = false)
{
if ($this->dataType != 'blob') {
throw new Exception("DataCollection: cannot call getExpandedDataTable with "
@@ -248,7 +248,7 @@ class DataCollection
$dataTableFactory = new DataTableFactory(
$this->dataNames, 'blob', $this->sitesId, $this->periods, $this->defaultRow);
- $dataTableFactory->expandDataTable($addMetadataSubTableId);
+ $dataTableFactory->expandDataTable($depth, $addMetadataSubTableId);
$dataTableFactory->useSubtable($idSubTable);
$index = $this->getArray($resultIndices);
diff --git a/core/Archive/DataTableFactory.php b/core/Archive/DataTableFactory.php
index 4fc42d662c..f45e1a3d58 100644
--- a/core/Archive/DataTableFactory.php
+++ b/core/Archive/DataTableFactory.php
@@ -15,8 +15,6 @@ use Piwik\Site;
use Piwik\DataTable;
use Piwik\DataTable\Row;
-const FIX_ME_OMG = 'this is a warning and reminder to fix this code ';
-
/**
* Creates a DataTable or Set instance based on an array
* index created by DataCollection.
@@ -53,6 +51,14 @@ class DataTableFactory
private $addMetadataSubtableId = false;
/**
+ * The maximum number of subtable levels to create when creating an expanded
+ * DataTable.
+ *
+ * @var int
+ */
+ private $maxSubtableDepth = null;
+
+ /**
* @see DataCollection::$sitesId.
*/
private $sitesId;
@@ -96,9 +102,10 @@ class DataTableFactory
* database to the in-memory DataTables as
* metadata or not.
*/
- public function expandDataTable($addMetadataSubtableId = false)
+ public function expandDataTable($maxSubtableDepth = null, $addMetadataSubtableId = false)
{
$this->expandDataTable = true;
+ $this->maxSubtableDepth = $maxSubtableDepth;
$this->addMetadataSubtableId = $addMetadataSubtableId;
}
@@ -292,9 +299,7 @@ class DataTableFactory
// would break.
if (count($this->dataNames) == 1) {
$name = reset($this->dataNames);
- $table->addRow(new Row(array(
- Row::COLUMNS => array($name => 0)
- )));
+ $table->addRow(new Row(array(Row::COLUMNS => array($name => 0))));
}
}
@@ -329,8 +334,19 @@ class DataTableFactory
* with blob values. This should hold every subtable blob for
* the loaded DataTable.
*/
- private function setSubtables($dataTable, $blobRow)
+ private function setSubtables($dataTable, $blobRow, $treeLevel = 0)
{
+ if ($this->maxSubtableDepth
+ && $treeLevel >= $this->maxSubtableDepth
+ ) {
+ // unset the subtables so DataTableManager doesn't throw
+ foreach ($dataTable->getRows() as $row) {
+ $row->removeSubtable();
+ }
+
+ return;
+ }
+
$dataName = reset($this->dataNames);
foreach ($dataTable->getRows() as $row) {
@@ -342,7 +358,7 @@ class DataTableFactory
$blobName = $dataName . "_" . $sid;
if (isset($blobRow[$blobName])) {
$subtable = DataTable::fromSerializedArray($blobRow[$blobName]);
- $this->setSubtables($subtable, $blobRow);
+ $this->setSubtables($subtable, $blobRow, $treeLevel + 1);
// we edit the subtable ID so that it matches the newly table created in memory
// NB: we dont overwrite the datatableid in the case we are displaying the table expanded.
@@ -366,9 +382,7 @@ class DataTableFactory
$periods = $this->periods;
$table->filter(function ($table) use ($periods) {
$table->metadata['site'] = new Site($table->metadata['site']);
- $table->metadata['period'] = empty($periods[$table->metadata['period']])
- ? FIX_ME_OMG
- : $periods[$table->metadata['period']];
+ $table->metadata['period'] = $periods[$table->metadata['period']];
});
}
@@ -381,12 +395,9 @@ class DataTableFactory
*/
private function prettifyIndexLabel($labelType, $label)
{
- if (empty($this->periods[$label])) {
- return $label; // BAD BUG FIXME
- }
if ($labelType == 'period') { // prettify period labels
return $this->periods[$label]->getPrettyString();
}
return $label;
}
-}
+} \ No newline at end of file
diff --git a/core/ArchiveProcessor/Period.php b/core/ArchiveProcessor/Period.php
index 6f7085c1a9..778b41f075 100644
--- a/core/ArchiveProcessor/Period.php
+++ b/core/ArchiveProcessor/Period.php
@@ -165,7 +165,7 @@ class Period extends ArchiveProcessor
$table->setColumnAggregationOperations($columnAggregationOperations);
}
- $data = $this->archiver->getDataTableExpanded($name, $idSubTable = null, $addMetadataSubtableId = false);
+ $data = $this->archiver->getDataTableExpanded($name, $idSubTable = null, $depth = null, $addMetadataSubtableId = false);
if ($data instanceof DataTable\Map) {
foreach ($data->getArray() as $date => $tableToSum) {
$table->addDataTable($tableToSum);
diff --git a/core/AssetManager.php b/core/AssetManager.php
index bdc311a720..2823373090 100644
--- a/core/AssetManager.php
+++ b/core/AssetManager.php
@@ -266,6 +266,7 @@ class AssetManager
'plugins/Zeitgeist/stylesheets/base.less',
'plugins/Zeitgeist/stylesheets/',
'plugins/',
+ 'plugins/Dashboard/stylesheets/dashboard.less',
);
return self::prioritySort($priorityCssOrdered, $cssFiles);
diff --git a/core/Controller.php b/core/Controller.php
index 5aac70c2e2..9b02395d81 100644
--- a/core/Controller.php
+++ b/core/Controller.php
@@ -176,8 +176,9 @@ abstract class Controller
*/
protected function getLastUnitGraph($currentModuleName, $currentControllerAction, $apiMethod)
{
- $view = ViewDataTable::factory('graphEvolution');
- $view->init($currentModuleName, $currentControllerAction, $apiMethod);
+ $view = ViewDataTable::factory(
+ 'graphEvolution', $apiMethod, $currentModuleName . '.' . $currentControllerAction, $forceDefault = true);
+ $view->show_goals = false;
return $view;
}
@@ -219,14 +220,14 @@ abstract class Controller
// initialize the graph and load the data
$view = $this->getLastUnitGraph($currentModuleName, $currentControllerAction, $apiMethod);
$view->columns_to_display = $columnsToDisplay;
- $view->selectable_columns = array_merge($view->selectable_columns, $selectableColumns);
+ $view->visualization_properties->selectable_columns =
+ array_merge($view->visualization_properties->selectable_columns ?: array(), $selectableColumns);
$view->translations += $translations;
if ($reportDocumentation) {
$view->documentation = $reportDocumentation;
}
- $view->main();
return $view;
}
diff --git a/core/DataTableVisualization.php b/core/DataTableVisualization.php
index ca41f65377..e70099ff03 100644
--- a/core/DataTableVisualization.php
+++ b/core/DataTableVisualization.php
@@ -14,43 +14,94 @@ namespace Piwik;
use Piwik\DataTable;
/**
- * TODO
+ * Base class for all DataTable visualizations. Different visualizations are used to
+ * handle different values of the viewDataTable query parameter. Each one will display
+ * DataTable data in a different way.
+ *
+ * TODO: must be more in depth
*/
abstract class DataTableVisualization
{
/**
- * TODO
+ * This event is used to gather all available DataTable visualizations. Callbacks
+ * should add visualization class names to the incoming array.
+ *
+ * Callback Signature: function (&$visualizations) {}
+ */
+ const GET_AVAILABLE_EVENT = 'DataTableVisualization.getAvailable';
+
+ /**
+ * Rendering function. Must return the view HTML.
+ *
+ * @param Piwik_DataTable|Piwik_DataTable_Map $dataTable The data.
+ * @param array $properties The view properties.
+ * @return string The visualization HTML.
*/
public abstract function render($dataTable, $properties);
/**
- * TODO
+ * Returns the array of view properties that a DataTable visualization will require
+ * to be both visible to client side JavaScript, and passed along as query parameters
+ * in every AJAX request.
+ *
+ * Derived DataTableVisualizations can specify client side parameters by declaring
+ * a static $clientSideParameters field.
+ *
+ * @return array
*/
- public static function getJavaScriptProperties()
+ public static function getClientSideParameters()
{
- if (isset(static::$javaScriptProperties)) {
- return static::$javaScriptProperties;
+ if (isset(static::$clientSideParameters)) {
+ $result = array();
+
+ $lineage = static::getThisVisualizationClassLineage();
+ foreach ($lineage as $klass) {
+ if (isset($klass::$clientSideParameters)) {
+ $result = array_merge($result, $clientSideParameters);
+ }
+ }
+
+ return $result;
} else {
return array();
}
}
/**
- * TODO
+ * Returns an array of view property names that a DataTable visualization will
+ * require to be visible to client side JavaScript. Unlike 'client side parameters',
+ * these will not be passed with AJAX requests as query parameters.
+ *
+ * Derived DataTableVisualizations can specify client side properties by declaring
+ * a static $clientSideProperties field.
+ *
+ * @return array
*/
- public static function getOverridableProperties()
+ public static function getClientSideProperties()
{
- if (isset(static::$overridableProperties)) {
- return static::$overridableProperties;
+ if (isset(static::$clientSideProperties)) {
+ $result = array();
+
+ $lineage = static::getThisVisualizationClassLineage();
+ foreach ($lineage as $klass) {
+ if (isset($klass::$clientSideProperties)) {
+ $result = array_merge($result, $clientSideProperties);
+ }
+ }
+
+ return $result;
} else {
return array();
}
}
/**
- * TODO
+ * Returns the viewDataTable ID for this DataTable visualization. Derived classes
+ * should declare a const ID field with the viewDataTable ID.
+ *
+ * @return string
*/
- public static function getViewDataTableId($view)
+ public static function getViewDataTableId()
{
if (defined('static::ID')) {
return static::ID;
@@ -58,4 +109,129 @@ abstract class DataTableVisualization
return Piwik::getUnnamespacedClassName($this);
}
}
-}
+
+ /**
+ * Returns the list of parents for a DataTableVisualization class excluding the
+ * DataTableVisualization class and above.
+ *
+ * @param string $klass The class name of the DataTableVisualization.
+ * @return array The list of parent classes in order from highest ancestor to
+ * the descended class.
+ */
+ public static function getVisualizationClassLineage($klass)
+ {
+ $klasses = array_merge(array($klass), class_parents($klass, $autoload = false));
+
+ $idx = array_search('Piwik\\DataTableVisualization', $klasses);
+ if ($idx !== false) {
+ unset($klasses[$idx]);
+ }
+
+ return array_reverse($klasses);
+ }
+
+ /**
+ * Returns the class lineage for this class. For use with late static bindings.
+ *
+ * @see self::getVisualizationClassLineage
+ *
+ * @return array
+ */
+ protected static function getThisVisualizationClassLineage()
+ {
+ return self::getVisualizationClassLineage(__CLASS__);
+ }
+
+ /**
+ * Returns the viewDataTable IDs of a visualization's class lineage.
+ *
+ * @see self::getVisualizationClassLineage
+ *
+ * @param string $klass The visualization class.
+ */
+ public static function getVisualizationIdsWithInheritance($klass)
+ {
+ $klasses = self::getVisualizationClassLineage($klass);
+ return array_map(array('Piwik\\Piwik', 'getUnnamespacedClassName'), $klasses);
+ }
+
+ /**
+ * Returns all registered visualization classes. Uses the 'DataTableVisualization.getAvailable'
+ * event to retrieve visualizations.
+ *
+ * @return array Array mapping visualization IDs with their associated visualization classes.
+ * @throws Exception If a visualization class does not exist or if a duplicate visualization ID
+ * is found.
+ */
+ public static function getAvailableVisualizations()
+ {
+ $visualizations = array();
+ Piwik_PostEvent(self::GET_AVAILABLE_EVENT, array(&$visualizations));
+
+ $result = array();
+ foreach ($visualizations as $viz) {
+ if (!class_exists($viz)) {
+ throw new \Exception(
+ "Invalid visualization class '$viz' found in DataTableVisualization.getAvailableVisualizations.");
+ }
+
+ if (is_subclass_of($viz, __CLASS__)) {
+ $vizId = $viz::getViewDataTableId();
+ if (isset($result[$vizId])) {
+ throw new Exception("Visualization ID '$vizId' is already in use!");
+ }
+
+ $result[$vizId] = $viz;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Returns all available visualizations that are not part of the CoreVisualizations plugin.
+ *
+ * @return array Array mapping visualization IDs with their associated visualization classes.
+ */
+ public static function getNonCoreVisualizations()
+ {
+ $result = array();
+ foreach (self::getAvailableVisualizations() as $vizId => $vizClass) {
+ if (strpos($vizClass, 'Piwik\\Plugins\\CoreVisualizations') === false) {
+ $result[$vizId] = $vizClass;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Returns an array mapping visualization IDs with information necessary for adding the
+ * visualizations to the footer of DataTable views.
+ *
+ * @param array $visualizations An array mapping visualization IDs w/ their associated classes.
+ * @return array
+ */
+ public static function getVisualizationInfoFor($visualizations)
+ {
+ $result = array();
+ foreach ($visualizations as $vizId => $vizClass) {
+ $result[$vizId] = array('table_icon' => $vizClass::FOOTER_ICON, 'title' => $vizClass::FOOTER_ICON_TITLE);
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the visualization class by it's viewDataTable ID.
+ *
+ * @param string $id The visualization ID.
+ * @return string The visualization class name.
+ * @throws Exception if $id is not a valid visualization ID.
+ */
+ public static function getClassFromId($id)
+ {
+ $visualizationClasses = self::getAvailableVisualizations();
+ if (!isset($visualizationClasses[$id])) {
+ throw new \Exception("Invalid DataTable visualization ID: '$id'.");
+ }
+ return $visualizationClasses[$id];
+ }
+} \ No newline at end of file
diff --git a/core/FrontController.php b/core/FrontController.php
index 4d4e814ebf..e0bb8258e2 100644
--- a/core/FrontController.php
+++ b/core/FrontController.php
@@ -131,7 +131,7 @@ class FrontController
} catch (Exception $e) {
$debugTrace = $e->getTraceAsString();
$message = Common::sanitizeInputValue($e->getMessage());
- Piwik_ExitWithMessage($message, '' /* $debugTrace */, true);
+ Piwik_ExitWithMessage($message, '' . $debugTrace , true);
}
}
diff --git a/core/JqplotDataGenerator.php b/core/JqplotDataGenerator.php
deleted file mode 100644
index 039370b251..0000000000
--- a/core/JqplotDataGenerator.php
+++ /dev/null
@@ -1,205 +0,0 @@
-<?php
-/**
- * Piwik - Open source web analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- * @category Piwik
- * @package Piwik
- */
-
-namespace Piwik;
-
-use Exception;
-use Piwik\Common;
-use Piwik\Metrics;
-use Piwik\DataTable;
-use Piwik\Visualization;
-
-/**
- * Generates JSON data used to configure and populate JQPlot graphs.
- *
- * Supports pie graphs, bar graphs and time serieses (aka, evolution graphs).
- */
-class JqplotDataGenerator
-{
- /**
- * View properties. @see Piwik_ViewDataTable for more info.
- *
- * @var array
- */
- protected $properties;
-
- /**
- * This object does most of the work in generating the JQPlot JSON data.
- *
- * @var Visualization\
- */
- protected $visualization;
-
- /**
- * Creates a new JqplotDataGenerator instance for a graph type and view properties.
- *
- * @param string $type 'pie', 'bar', or 'evolution'
- * @param array $properties The view properties.
- * @throws \Exception
- * @return \Piwik\JqplotDataGenerator
- */
- public static function factory($type, $properties)
- {
- switch ($type) {
- case 'evolution':
- return new JqplotDataGenerator\Evolution($properties);
- case 'pie':
- $visualization = new Visualization\Chart\Pie();
- return new JqplotDataGenerator($visualization, $properties);
- case 'bar':
- $visualization = new Visualization\Chart\VerticalBar();
- return new JqplotDataGenerator($visualization, $properties);
- default:
- throw new Exception("Unknown JqplotDataGenerator type '$type'.");
- }
- }
-
- /**
- * Constructor.
- *
- * @param Visualization\ $visualization
- * @param array $properties
- */
- public function __construct($visualization, $properties)
- {
- $this->visualization = $visualization;
- $this->properties = $properties;
- }
-
- /**
- * Generates JSON graph data and returns it.
- *
- * @param DataTable|DataTable\Map $dataTable
- * @return string
- */
- public function generate($dataTable)
- {
- if (!empty($this->properties['graph_limit'])) {
- $offsetStartSummary = $this->properties['graph_limit'] - 1;
- $sortColumn = !empty($this->properties['filter_sort_column'])
- ? $this->properties['filter_sort_column']
- : Metrics::INDEX_NB_VISITS;
-
- $dataTable->filter(
- 'AddSummaryRow', array($offsetStartSummary, Piwik_Translate('General_Others'), $sortColumn));
- }
-
- if ($dataTable->getRowsCount() > 0) {
- // if addTotalRow was called in GenerateGraphHTML, add a row containing totals of
- // different metrics
- if (!empty($this->properties['add_total_row'])) {
- $dataTable->queueFilter('AddSummaryRow', array(0, Piwik_Translate('General_Total'), null, false));
- }
-
- $this->initChartObjectData($dataTable);
- }
-
- $this->visualization->customizeChartProperties();
-
- return $this->visualization->render();
- }
-
- /**
- * @param DataTable|DataTable\Map $dataTable
- */
- protected function initChartObjectData($dataTable)
- {
- $dataTable->applyQueuedFilters();
-
- // We apply a filter to the DataTable, decoding the label column (useful for keywords for example)
- $dataTable->filter('ColumnCallbackReplace', array('label', 'urldecode'));
-
- $xLabels = $dataTable->getColumn('label');
-
- $columnNames = $this->properties['columns_to_display'];
- if (($labelColumnIndex = array_search('label', $columnNames)) !== false) {
- unset($columnNames[$labelColumnIndex]);
- }
-
- $columnNameToTranslation = $columnNameToValue = array();
- foreach ($columnNames as $columnName) {
- $columnNameToTranslation[$columnName] = @$this->properties['translations'][$columnName];
-
- $columnNameToValue[$columnName] = $dataTable->getColumn($columnName);
- }
-
- $visualization = $this->visualization;
- $visualization->setAxisXLabels($xLabels);
- $visualization->setAxisYValues($columnNameToValue);
- $visualization->setAxisYLabels($columnNameToTranslation);
- $visualization->setAxisYUnit($this->properties['y_axis_unit']);
- $visualization->setDisplayPercentageInTooltip($this->properties['display_percentage_in_tooltip']);
-
- // show_all_ticks is not real query param, it is set by GenerateGraphHTML.
- if ($this->properties['show_all_ticks']) {
- $visualization->showAllTicks();
- }
-
- $units = $this->getUnitsForColumnsToDisplay();
- $visualization->setAxisYUnits($units);
-
- $this->addSeriesPickerToView();
- }
-
- protected function getUnitsForColumnsToDisplay()
- {
- // derive units from column names
- $units = $this->deriveUnitsFromRequestedColumnNames();
- if (!empty($this->properties['y_axis_unit'])) {
- // force unit to the value set via $this->setAxisYUnit()
- foreach ($units as &$unit) {
- $unit = $this->properties['y_axis_unit'];
- }
- }
-
- // the bar charts contain the labels a first series
- // this series has to be removed from the units
- if ($this->visualization instanceof Visualization\Chart\VerticalBar) {
- array_shift($units);
- }
-
- return $units;
- }
-
- private function deriveUnitsFromRequestedColumnNames()
- {
- $idSite = Common::getRequestVar('idSite', null, 'int');
-
- $units = array();
- foreach ($this->properties['columns_to_display'] as $columnName) {
- $derivedUnit = Metrics::getUnit($columnName, $idSite);
- $units[$columnName] = empty($derivedUnit) ? false : $derivedUnit;
- }
- return $units;
- }
-
- /**
- * Used in initChartObjectData to add the series picker config to the view object
- */
- protected function addSeriesPickerToView()
- {
- if (count($this->properties['selectable_columns'])
- && Common::getRequestVar('showSeriesPicker', $this->properties['show_series_picker']) == 1
- ) {
- $selectableColumns = array();
- foreach ($this->properties['selectable_columns'] as $column) {
- $selectableColumns[] = array(
- 'column' => $column,
- 'translation' => @$this->properties['translations'][$column],
- 'displayed' => in_array($column, $this->properties['columns_to_display'])
- );
- }
-
- $this->visualization->setSelectableColumns(
- $selectableColumns, $this->properties['allow_multi_select_series_picker']);
- }
- }
-}
diff --git a/core/JqplotDataGenerator/Evolution.php b/core/JqplotDataGenerator/Evolution.php
deleted file mode 100644
index dab684cb0a..0000000000
--- a/core/JqplotDataGenerator/Evolution.php
+++ /dev/null
@@ -1,272 +0,0 @@
-<?php
-/**
- * Piwik - Open source web analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- * @category Piwik
- * @package Piwik
- */
-namespace Piwik\JqplotDataGenerator;
-
-use Piwik\Piwik;
-use Piwik\Common;
-use Piwik\DataTable;
-use Piwik\ViewDataTable;
-use Piwik\Url;
-use Piwik\JqplotDataGenerator;
-
-/**
- * Generates JQPlot JSON data/config for evolution graphs.
- */
-class Evolution extends JqplotDataGenerator
-{
- protected $rowPickerConfig = array();
-
- /**
- * Constructor.
- *
- * @param array $properties
- */
- public function __construct($properties)
- {
- parent::__construct(new \Piwik\Visualization\Chart\Evolution(), $properties);
- }
-
- /**
- * @param DataTable|DataTable\Map $dataTable
- */
- protected function initChartObjectData($dataTable)
- {
- // if the loaded datatable is a simple DataTable, it is most likely a plugin plotting some custom data
- // we don't expect plugin developers to return a well defined Set
- if ($dataTable instanceof DataTable) {
- parent::initChartObjectData($dataTable);
- return;
- }
-
- $dataTable->applyQueuedFilters();
-
- // the X label is extracted from the 'period' object in the table's metadata
- $xLabels = $uniqueIdsDataTable = array();
- foreach ($dataTable->getArray() as $idDataTable => $metadataDataTable) {
- //eg. "Aug 2009"
- $xLabels[] = $metadataDataTable->getMetadata('period')->getLocalizedShortString();
- // we keep track of all unique data table that we need to set a Y value for
- $uniqueIdsDataTable[] = $idDataTable;
- }
-
- $idSite = Common::getRequestVar('idSite', null, 'int');
- $requestedColumnNames = $this->properties['columns_to_display'];
- $units = $this->getUnitsForColumnsToDisplay();
-
- $yAxisLabelToUnit = array();
- $yAxisLabelToValue = array();
- foreach ($dataTable->getArray() as $idDataTable => $childTable) {
- foreach ($childTable->getRows() as $row) {
- $rowLabel = $row->getColumn('label');
-
- // put together configuration for row picker.
- // do this for every data table in the array because rows do not
- // have to present for each date.
- if ($this->properties['row_picker_mach_rows_by'] !== false) {
- $rowVisible = $this->handleRowForRowPicker($rowLabel);
- if (!$rowVisible) {
- continue;
- }
- }
-
- // build data for request columns
- foreach ($requestedColumnNames as $requestedColumnName) {
- $yAxisLabel = $this->getSeriesLabel($rowLabel, $requestedColumnName);
- if (($columnValue = $row->getColumn($requestedColumnName)) !== false) {
- $yAxisLabelToValue[$yAxisLabel][$idDataTable] = $columnValue;
- $yAxisLabelToUnit[$yAxisLabel] = $units[$requestedColumnName];
- }
- }
- }
- }
-
- // make sure all column values are set to at least zero (no gap in the graph)
- $yAxisLabelToValueCleaned = array();
- foreach ($uniqueIdsDataTable as $uniqueIdDataTable) {
- foreach ($yAxisLabelToValue as $yAxisLabel => $idDataTableToColumnValue) {
- if (isset($idDataTableToColumnValue[$uniqueIdDataTable])) {
- $columnValue = $idDataTableToColumnValue[$uniqueIdDataTable];
- } else {
- $columnValue = 0;
- }
- $yAxisLabelToValueCleaned[$yAxisLabel][] = $columnValue;
- }
- }
-
- $visualization = $this->visualization;
- $visualization->setAxisXLabels($xLabels);
- $visualization->setAxisYValues($yAxisLabelToValueCleaned);
- $visualization->setAxisYUnits($yAxisLabelToUnit);
-
- $countGraphElements = $dataTable->getRowsCount();
- $dataTables = $dataTable->getArray();
- $firstDatatable = reset($dataTables);
-
- /** @var \Piwik\Period $period */
- $period = $firstDatatable->getMetadata('period');
-
- $stepSize = $this->getXAxisStepSize($period->getLabel(), $countGraphElements);
- $visualization->setXSteps($stepSize);
-
- if ($this->isLinkEnabled()) {
- $axisXOnClick = array();
- $queryStringAsHash = $this->getQueryStringAsHash();
- foreach ($dataTable->getArray() as $idDataTable => $metadataDataTable) {
- $period = $metadataDataTable->getMetadata('period');
- $dateInUrl = $period->getDateStart();
- $parameters = array(
- 'idSite' => $idSite,
- 'period' => $period->getLabel(),
- 'date' => $dateInUrl->toString(),
- 'segment' => \Piwik\API\Request::getRawSegmentFromRequest()
- );
- $hash = '';
- if (!empty($queryStringAsHash)) {
- $hash = '#' . Url::getQueryStringFromParameters($queryStringAsHash + $parameters);
- }
- $link = 'index.php?' .
- Url::getQueryStringFromParameters(array(
- 'module' => 'CoreHome',
- 'action' => 'index',
- ) + $parameters)
- . $hash;
- $axisXOnClick[] = $link;
- }
- $visualization->setAxisXOnClick($axisXOnClick);
- }
-
- $this->addSeriesPickerToView();
-
- // configure the row picker
- if ($this->properties['row_picker_mach_rows_by'] !== false) {
- $visualization->setSelectableRows(array_values($this->rowPickerConfig));
- }
- }
-
- /**
- * This method is called for every row of every table in the DataTable_Array.
- * It incrementally builds the row picker configuration and determines whether
- * the row is initially visible or not.
- * @param string $rowLabel
- * @return bool
- */
- private function handleRowForRowPicker(&$rowLabel)
- {
- // determine whether row is visible
- $isVisible = true;
- if ($this->properties['row_picker_mach_rows_by'] == 'label') {
- $isVisible = in_array($rowLabel, $this->properties['row_picker_visible_rows']);
- }
-
- // build config
- if (!isset($this->rowPickerConfig[$rowLabel])) {
- $this->rowPickerConfig[$rowLabel] = array(
- 'label' => $rowLabel,
- 'matcher' => $rowLabel,
- 'displayed' => $isVisible
- );
- }
-
- return $isVisible;
- }
-
- /**
- * Derive the series label from the row label and the column name.
- * If the row label is set, both the label and the column name are displayed.
- * @param string $rowLabel
- * @param string $columnName
- * @return string
- */
- private function getSeriesLabel($rowLabel, $columnName)
- {
- $metricLabel = @$this->properties['translations'][$columnName];
-
- if ($rowLabel !== false) {
- // eg. "Yahoo! (Visits)"
- $label = "$rowLabel ($metricLabel)";
- } else {
- // eg. "Visits"
- $label = $metricLabel;
- }
-
- return $label;
- }
-
- /**
- * We link the graph dots to the same report as currently being displayed (only the date would change).
- *
- * In some cases the widget is loaded within a report that doesn't exist as such.
- * For example, the dashboards loads the 'Last visits graph' widget which can't be directly linked to.
- * Instead, the graph must link back to the dashboard.
- *
- * In other cases, like Visitors>Overview or the Goals graphs, we can link the graph clicks to the same report.
- *
- * To detect whether or not we can link to a report, we simply check if the current URL from which it was loaded
- * belongs to the menu or not. If it doesn't belong to the menu, we do not append the hash to the URL,
- * which results in loading the dashboard.
- *
- * @return array Query string array to append to the URL hash or false if the dashboard should be displayed
- */
- private function getQueryStringAsHash()
- {
- $queryString = Url::getArrayFromCurrentQueryString();
- $piwikParameters = array('idSite', 'date', 'period', 'XDEBUG_SESSION_START', 'KEY');
- foreach ($piwikParameters as $parameter) {
- unset($queryString[$parameter]);
- }
- if (\Piwik\Menu\Main::getInstance()->isUrlFound($queryString)) {
- return $queryString;
- }
- return false;
- }
-
- private function isLinkEnabled()
- {
- static $linkEnabled;
- if (!isset($linkEnabled)) {
- // 1) Custom Date Range always have link disabled, otherwise
- // the graph data set is way too big and fails to display
- // 2) disableLink parameter is set in the Widgetize "embed" code
- $linkEnabled = !Common::getRequestVar('disableLink', 0, 'int')
- && Common::getRequestVar('period', 'day') != 'range';
- }
- return $linkEnabled;
- }
-
- private function getXAxisStepSize($periodLabel, $countGraphElements)
- {
- // when the number of elements plotted can be small, make sure the X legend is useful
- if ($countGraphElements <= 7) {
- return 1;
- }
-
- switch ($periodLabel) {
- case 'day':
- $steps = 5;
- break;
- case 'week':
- $steps = 4;
- break;
- case 'month':
- $steps = 5;
- break;
- case 'year':
- $steps = 5;
- break;
- default:
- $steps = 5;
- break;
- }
-
- $paddedCount = $countGraphElements + 2; // pad count so last label won't be cut off
- return ceil($paddedCount / $steps);
- }
-}
diff --git a/core/PluginsManager.php b/core/PluginsManager.php
index 99f3a7ae37..bbfec52d9a 100644
--- a/core/PluginsManager.php
+++ b/core/PluginsManager.php
@@ -43,6 +43,7 @@ class PluginsManager
'CoreUpdater',
'CoreAdminHome',
'CorePluginsAdmin',
+ 'CoreVisualizations',
'Installation',
'SitesManager',
'UsersManager',
diff --git a/core/Tracker/Request.php b/core/Tracker/Request.php
index 668d23fa1e..595c3e9f09 100644
--- a/core/Tracker/Request.php
+++ b/core/Tracker/Request.php
@@ -27,6 +27,8 @@ class Request
*/
protected $params;
+ protected $forcedVisitorId = false;
+
public function __construct($params, $tokenAuth = false)
{
if (!is_array($params)) {
@@ -434,7 +436,9 @@ class Request
public function setForceIp($ip)
{
- $this->enforcedIp = $ip;
+ if(!empty($ip)) {
+ $this->enforcedIp = $ip;
+ }
}
public function setForceDateTime($dateTime)
@@ -442,12 +446,16 @@ class Request
if (!is_numeric($dateTime)) {
$dateTime = strtotime($dateTime);
}
- $this->timestamp = $dateTime;
+ if(!empty($dateTime)) {
+ $this->timestamp = $dateTime;
+ }
}
public function setForcedVisitorId($visitorId)
{
- $this->forcedVisitorId = $visitorId;
+ if(!empty($visitorId)) {
+ $this->forcedVisitorId = $visitorId;
+ }
}
public function getForcedVisitorId()
diff --git a/core/Twig.php b/core/Twig.php
index f8325a1ce5..4b32e4931c 100644
--- a/core/Twig.php
+++ b/core/Twig.php
@@ -73,6 +73,15 @@ class Twig
$this->addFunction_loadJavascriptTranslations();
$this->addFunction_sparkline();
$this->addFunction_postEvent();
+ $this->addFunction_isPluginLoaded();
+ }
+
+ protected function addFunction_isPluginLoaded()
+ {
+ $isPluginLoadedFunction = new Twig_SimpleFunction('isPluginLoaded', function ($pluginName) {
+ return PluginsManager::getInstance()->isPluginLoaded($pluginName);
+ });
+ $this->twig->addFunction($isPluginLoadedFunction);
}
protected function addFunction_includeAssets()
diff --git a/core/ViewDataTable.php b/core/ViewDataTable.php
index e47bb0d3e8..952feab283 100644
--- a/core/ViewDataTable.php
+++ b/core/ViewDataTable.php
@@ -25,6 +25,7 @@ use Piwik\ViewDataTable\Properties;
use Piwik\ViewDataTable\VisualizationPropertiesProxy;
use Piwik_API_API;
use Piwik\PluginsManager;
+use Piwik\DataTableVisualization;
/**
* This class is used to load (from the API) and customize the output of a given DataTable.
@@ -61,10 +62,11 @@ use Piwik\PluginsManager;
class ViewDataTable
{
/**
- * TODO
- * TODO: change to private
+ * The class name of the visualization to use.
+ *
+ * @var string|null
*/
- protected $visualizationClass;
+ private $visualizationClass;
/**
* Cache for getAllReportDisplayProperties result.
@@ -74,13 +76,6 @@ class ViewDataTable
public static $reportPropertiesCache = null;
/**
- * Flag used to make sure the main() is only executed once
- *
- * @var bool
- */
- protected $mainAlreadyExecuted = false;
-
- /**
* Array of properties that are available in the view
* Used to store UI properties, eg. "show_footer", "show_search", etc.
*
@@ -125,68 +120,55 @@ class ViewDataTable
/**
* Default constructor.
*/
- public function __construct($visualizationClass = null)
+ public function __construct($currentControllerAction,
+ $apiMethodToRequestDataTable,
+ $viewProperties = array(),
+ $visualizationId = null)
{
+ $visualizationClass = $visualizationId ? DataTableVisualization::getClassFromId($visualizationId) : null;
$this->visualizationClass = $visualizationClass;
- $this->viewProperties['visualization_properties'] = new VisualizationPropertiesProxy(null);
- $this->viewProperties['datatable_template'] = '@CoreHome/_dataTable';
- $this->viewProperties['show_goals'] = false;
- $this->viewProperties['show_ecommerce'] = false;
- $this->viewProperties['show_search'] = true;
- $this->viewProperties['show_table'] = true;
- $this->viewProperties['show_table_all_columns'] = true;
- $this->viewProperties['show_all_views_icons'] = true;
- $this->viewProperties['show_active_view_icon'] = true;
- $this->viewProperties['hide_annotations_view'] = true;
- $this->viewProperties['show_bar_chart'] = true;
- $this->viewProperties['show_pie_chart'] = true;
- $this->viewProperties['show_tag_cloud'] = true;
- $this->viewProperties['show_export_as_image_icon'] = false;
- $this->viewProperties['show_export_as_rss_feed'] = true;
- $this->viewProperties['show_exclude_low_population'] = true;
- $this->viewProperties['show_offset_information'] = true;
- $this->viewProperties['show_pagination_control'] = true;
- $this->viewProperties['show_limit_control'] = false;
- $this->viewProperties['show_footer'] = true;
- $this->viewProperties['show_related_reports'] = true;
- $this->viewProperties['exportLimit'] = Config::getInstance()->General['API_datatable_default_limit'];
- $this->viewProperties['highlight_summary_row'] = false;
+ $this->viewProperties['visualization_properties'] = new VisualizationPropertiesProxy($visualizationClass);
$this->viewProperties['metadata'] = array();
- $this->viewProperties['relatedReports'] = array();
- $this->viewProperties['title'] = 'unknown';
- $this->viewProperties['tooltip_metadata_name'] = false;
- $this->viewProperties['enable_sort'] = true;
- $this->viewProperties['disable_generic_filters'] = false;
- $this->viewProperties['disable_queued_filters'] = false;
- $this->viewProperties['keep_summary_row'] = false;
- $this->viewProperties['filter_excludelowpop'] = false;
- $this->viewProperties['filter_excludelowpop_value'] = false;
- $this->viewProperties['filter_pattern'] = false;
- $this->viewProperties['filter_column'] = false;
- $this->viewProperties['filter_limit'] = false;
- $this->viewProperties['filter_sort_column'] = false;
- $this->viewProperties['filter_sort_order'] = false;
- $this->viewProperties['custom_parameters'] = array();
- $this->viewProperties['translations'] = array_merge(
- Metrics::getDefaultMetrics(),
- Metrics::getDefaultProcessedMetrics()
- );
- $this->viewProperties['request_parameters_to_modify'] = array();
- $this->viewProperties['documentation'] = false;
- $this->viewProperties['subtable_controller_action'] = false;
- $this->viewProperties['datatable_css_class'] = $this->getDefaultDataTableCssClass();
- $this->viewProperties['selectable_columns'] = array(); // TODO: only valid for graphs... shouldn't be here.
+ $this->viewProperties['translations'] = array();
$this->viewProperties['filters'] = array();
- $this->viewProperties['show_series_picker'] = true; // TODO: only for graphs.
- $this->viewProperties['graph_limit'] = false; // TODO: only for graph.
- $this->viewProperties['columns_to_display'] = array();
-
- $columns = Common::getRequestVar('columns', false);
- if ($columns !== false) {
- $this->viewProperties['columns_to_display'] = Piwik::getArrayFromApiParameter($columns);
- array_unshift($this->viewProperties['columns_to_display'], 'label');
+ $this->viewProperties['related_reports'] = array();
+
+ list($currentControllerName, $currentControllerAction) = explode('.', $currentControllerAction);
+ $this->currentControllerName = $currentControllerName;
+ $this->currentControllerAction = $currentControllerAction;
+
+ $this->setDefaultProperties();
+
+ foreach ($viewProperties as $name => $value) {
+ $this->setViewProperty($name, $value);
}
+
+ $queryParams = Url::getArrayFromCurrentQueryString();
+ foreach ($this->getClientSideProperties() as $name) {
+ if (isset($queryParams[$name])) {
+ $this->setViewProperty($name, $queryParams[$name]);
+ }
+ }
+
+ $this->idSubtable = Common::getRequestVar('idSubtable', false, 'int');
+ $this->viewProperties['show_footer_icons'] = ($this->idSubtable == false);
+ $this->viewProperties['apiMethodToRequestDataTable'] = $apiMethodToRequestDataTable;
+
+ $this->viewProperties['report_id'] = $currentControllerName . '.' . $currentControllerAction;
+ $this->viewProperties['self_url'] = $this->getBaseReportUrl($currentControllerName, $currentControllerAction);
+
+ // the exclude low population threshold value is sometimes obtained by requesting data.
+ // to avoid issuing unecessary requests when display properties are determined by metadata,
+ // we allow it to be a closure.
+ if (isset($this->viewProperties['filter_excludelowpop_value'])
+ && $this->viewProperties['filter_excludelowpop_value'] instanceof \Closure
+ ) {
+ $function = $this->viewProperties['filter_excludelowpop_value'];
+ $this->viewProperties['filter_excludelowpop_value'] = $function();
+ }
+
+ $this->loadDocumentation();
}
/**
@@ -219,30 +201,11 @@ class ViewDataTable
}
/**
- * TODO
+ * Hack to allow property access in Twig (w/ property name checking).
*/
- public function main()
+ public function __call($name, $arguments)
{
- if ($this->mainAlreadyExecuted) {
- return;
- }
- $this->mainAlreadyExecuted = true;
-
- $visualization = new $this->visualizationClass($this);
-
- try {
- $this->loadDataTableFromAPI();
- } catch (NoAccessException $e) {
- throw $e;
- } catch (\Exception $e) {
- Piwik::log("Failed to get data from API: " . $e->getMessage());
-
- $this->loadingError = array('message' => $e->getMessage());
- }
-
- $this->postDataTableLoadedFromAPI();
-
- $this->view = $this->buildView($visualization);
+ return $this->$name;
}
/**
@@ -250,7 +213,7 @@ class ViewDataTable
*
* @return string
*/
- protected function getViewDataTableId()
+ public function getViewDataTableId()
{
$klass = $this->visualizationClass;
return $klass::getViewDataTableId($this);
@@ -268,81 +231,36 @@ class ViewDataTable
* @param string|bool $controllerAction
* @return ViewDataTable
*/
- static public function factory($defaultType = null, $apiAction = false, $controllerAction = false)
+ static public function factory($defaultType = null, $apiAction = false, $controllerAction = false, $forceDefault = false)
{
- if ($apiAction !== false) {
- $defaultProperties = self::getDefaultPropertiesForReport($apiAction);
- if (isset($defaultProperties['default_view_type'])) {
- $defaultType = $defaultProperties['default_view_type'];
- }
-
- if ($controllerAction === false) {
- $controllerAction = $apiAction;
- }
+ if ($controllerAction === false) {
+ $controllerAction = $apiAction;
}
- if ($defaultType === null) {
- $defaultType = 'table';
+ $defaultProperties = self::getDefaultPropertiesForReport($apiAction);
+ if (!empty($defaultProperties['default_view_type'])
+ && !$forceDefault
+ ) {
+ $defaultType = $defaultProperties['default_view_type'];
}
- $type = Common::getRequestVar('viewDataTable', $defaultType, 'string');
- switch ($type) {
- case 'cloud':
- $result = new ViewDataTable\Cloud();
- break;
-
- case 'graphPie':
- $result = new ViewDataTable('\\Piwik\\Visualization\\JqplotGraph\\Pie');
- break;
-
- case 'graphVerticalBar':
- $result = new ViewDataTable('\\Piwik\\Visualization\\JqplotGraph\\Bar');
- break;
-
- case 'graphEvolution':
- $result = new ViewDataTable('\\Piwik\\Visualization\\JqplotGraph\\Evolution');
- break;
-
- case 'sparkline':
- $result = new ViewDataTable\Sparkline();
- break;
-
- case 'tableAllColumns': // for backwards compatibility TODO: shouldn't require this viewdatatable... (same for Goals)
- $result = new ViewDataTable('\\Piwik\\Visualization\\HtmlTable');
- $result->show_extra_columns = true;
- break;
-
- case 'tableGoals': // for backwards compatibility
- $result = new ViewDataTable('\\Piwik\\Visualization\\HtmlTable');
- $result->show_goals_columns = true;
- break;
-
- case 'table':
- default:
- $result = new ViewDataTable('\\Piwik\\Visualization\\HtmlTable');
- break;
- }
-
- if ($apiAction !== false) {
- list($plugin, $controllerAction) = explode('.', $controllerAction);
-
- $subtableAction = $controllerAction;
- if (isset($defaultProperties['subtable_action'])) {
- $subtableAction = $defaultProperties['subtable_action'];
- }
-
- $result->init($plugin, $controllerAction, $apiAction, $subtableAction, $defaultProperties);
+ $type = Common::getRequestVar('viewDataTable', $defaultType ?: 'table', 'string');
+
+ if ($type == 'sparkline') {
+ $result = new ViewDataTable\Sparkline($controllerAction, $apiAction, $defaultProperties);
+ } else {
+ $result = new ViewDataTable($controllerAction, $apiAction, $defaultProperties, $type);
}
return $result;
}
/**
- * Returns the list of view properties that can be overridden by query parameters.
+ * TODO
*
* @return array
*/
- public function getOverridableProperties()
+ public function getClientSideProperties()
{
$result = array(
'show_search',
@@ -350,7 +268,6 @@ class ViewDataTable
'show_table_all_columns',
'show_all_views_icons',
'show_active_view_icon',
- 'hide_annotations_view',
'show_barchart',
'show_piechart',
'show_tag_cloud',
@@ -361,12 +278,12 @@ class ViewDataTable
'show_pagination_control',
'show_footer',
'show_related_reports',
- 'columns'
+ 'keep_summary_row',
);
if ($this->visualizationClass) {
$klass = $this->visualizationClass;
- $result = array_merge($result, $klass::getOverridableProperties());
+ $result = array_merge($result, $klass::getClientSideProperties());
}
return $result;
@@ -378,13 +295,12 @@ class ViewDataTable
*
* @return array
*/
- public function getJavaScriptProperties()
+ public function getClientSideParameters()
{
$result = array(
'enable_sort',
'disable_generic_filters',
'disable_queued_filters',
- 'keep_summary_row',
'filter_excludelowpop',
'filter_excludelowpop_value',
'filter_pattern',
@@ -396,103 +312,12 @@ class ViewDataTable
if ($this->visualizationClass) {
$klass = $this->visualizationClass;
- $result = array_merge($result, $klass::getJavaScriptProperties());
+ $result = array_merge($result, $klass::getClientSideParameters());
}
return $result;
}
- /**
- * Inits the object given the $currentControllerName, $currentControllerAction of
- * the calling controller action, eg. 'Referers' 'getLongListOfKeywords'.
- * The initialization also requires the $apiMethodToRequestDataTable of the API method
- * to call in order to get the DataTable, eg. 'Referers.getKeywords'.
- * The optional $controllerActionCalledWhenRequestSubTable defines the method name of the API to call when there is a idSubtable.
- * This value would be used by the javascript code building the GET request to the API.
- *
- * Example:
- * For the keywords listing, a click on the row loads the subTable of the Search Engines for this row.
- * In this case $controllerActionCalledWhenRequestSubTable = 'getSearchEnginesFromKeywordId'.
- * The GET request will hit 'Referers.getSearchEnginesFromKeywordId'.
- *
- * @param string $currentControllerName eg. 'Referers'
- * @param string $currentControllerAction eg. 'getKeywords'
- * @param string $apiMethodToRequestDataTable eg. 'Referers.getKeywords'
- * @param string $controllerActionCalledWhenRequestSubTable eg. 'getSearchEnginesFromKeywordId'
- * @param array $defaultProperties
- */
- public function init($currentControllerName,
- $currentControllerAction,
- $apiMethodToRequestDataTable,
- $controllerActionCalledWhenRequestSubTable = null,
- $defaultProperties = array())
- {
- $this->currentControllerName = $currentControllerName;
- $this->currentControllerAction = $currentControllerAction;
- $this->viewProperties['subtable_controller_action'] = $controllerActionCalledWhenRequestSubTable;
- $this->idSubtable = Common::getRequestVar('idSubtable', false, 'int');
-
- foreach ($defaultProperties as $name => $value) {
- $this->setViewProperty($name, $value);
- }
-
- $queryParams = Url::getArrayFromCurrentQueryString();
- foreach ($this->getOverridableProperties() as $name) {
- if (isset($queryParams[$name])) {
- $this->setViewProperty($name, $queryParams[$name]);
- }
- }
-
- $this->viewProperties['show_footer_icons'] = ($this->idSubtable == false);
- $this->viewProperties['apiMethodToRequestDataTable'] = $apiMethodToRequestDataTable;
-
- $this->viewProperties['report_id'] = $currentControllerName . '.' . $currentControllerAction;
- $this->viewProperties['self_url'] = $this->getBaseReportUrl($currentControllerName, $currentControllerAction);
-
- // the exclude low population threshold value is sometimes obtained by requesting data.
- // to avoid issuing unecessary requests when display properties are determined by metadata,
- // we allow it to be a closure.
- if (isset($this->viewProperties['filter_excludelowpop_value'])
- && $this->viewProperties['filter_excludelowpop_value'] instanceof \Closure
- ) {
- $function = $this->viewProperties['filter_excludelowpop_value'];
- $this->viewProperties['filter_excludelowpop_value'] = $function();
- }
-
- $this->loadDocumentation();
- }
-
- /**
- * Forces the View to use a given template.
- * Usually the template to use is set in the specific ViewDataTable_*
- * eg. 'CoreHome/templates/cloud'
- * But some users may want to force this template to some other value
- *
- * TODO: after visualization refactor, should remove this.
- *
- * @param string $tpl eg .'@MyPlugin/templateToUse'
- */
- public function setTemplate($tpl)
- {
- $this->viewProperties['datatable_template'] = $tpl;
- }
-
- /**
- * Returns the View_Interface.
- * You can then call render() on this object.
- *
- * @return View\ViewInterface
- * @throws \Exception if the view object was not created
- */
- public function getView()
- {
- if (is_null($this->view)) {
- throw new \Exception('The $this->view object has not been created.
- It should be created in the main() method of the ViewDataTable_* subclass you are using.');
- }
- return $this->view;
- }
-
public function getCurrentControllerAction()
{
return $this->currentControllerAction;
@@ -561,7 +386,7 @@ class ViewDataTable
/**
* Sets a view property by name. This function handles special view properties
- * like 'translations' & 'relatedReports' that store arrays.
+ * like 'translations' & 'related_reports' that store arrays.
*
* @param string $name
* @param mixed $value For array properties, $value can be a comma separated string.
@@ -579,10 +404,40 @@ class ViewDataTable
|| $name == 'filters'
) {
$this->viewProperties[$name] = array_merge($this->viewProperties[$name], $value);
- } else if ($name == 'relatedReports') {
+ } else if ($name == 'related_reports') { // TODO: should process after (in overrideViewProperties)
$this->addRelatedReports($value);
- } else {
+ } else if ($name == 'visualization_properties') {
+ $this->setVisualizationPropertiesFromMetadata($value);
+ } else if (Properties::isCoreViewProperty($name)) {
$this->viewProperties[$name] = $value;
+ } else {
+ $report = $this->currentControllerName . '.' . $this->currentControllerAction;
+ throw new \Exception("Invalid view property '$name' specified in view property metadata for '$report'.");
+ }
+ }
+
+ /**
+ * TODO
+ */
+ private function setVisualizationPropertiesFromMetadata($properties)
+ {
+ if ($this->visualizationClass === null) {
+ return null;
+ }
+
+ $visualizationIds = DataTableVisualization::getVisualizationIdsWithInheritance($this->visualizationClass);
+ foreach ($visualizationIds as $visualizationId) {
+ if (empty($properties[$visualizationId])) {
+ continue;
+ }
+
+ foreach ($properties[$visualizationId] as $key => $value) {
+ if (Properties::isCoreViewProperty($key)) {
+ $this->viewProperties[$key] = $value;
+ } else {
+ $this->viewProperties['visualization_properties']->$key = $value;
+ }
+ }
}
}
@@ -658,8 +513,6 @@ class ViewDataTable
*/
protected function postDataTableLoadedFromAPI()
{
- $this->overrideViewProperties();
-
if (empty($this->dataTable)) {
return false;
}
@@ -899,7 +752,7 @@ class ViewDataTable
// case of the filter without default values and parameters set directly in this class
// for example setExcludeLowPopulation
// we go through all the $this->viewProperties array and set the variables not set yet
- foreach ($this->getJavaScriptProperties() as $name) {
+ foreach ($this->getClientSideParameters() as $name) {
if (!isset($javascriptVariablesToSet[$name])
&& !empty($this->viewProperties[$name])
) {
@@ -962,6 +815,20 @@ class ViewDataTable
}
/**
+ * TODO
+ */
+ private function getClientSidePropertiesToSet()
+ {
+ $result = array();
+ foreach ($this->getClientSideProperties() as $name) {
+ if (isset($this->viewProperties[$name])) {
+ $result[$name] = $this->viewProperties[$name];
+ }
+ }
+ return $result;
+ }
+
+ /**
* Returns, for a given parameter, the value of this parameter in the REQUEST array.
* If not set, returns the default value for this parameter @see getDefault()
*
@@ -1038,7 +905,7 @@ class ViewDataTable
}
$url = $this->getBaseReportUrl($module, $action, $queryParams);
- $this->viewProperties['relatedReports'][$url] = $title;
+ $this->viewProperties['related_reports'][$url] = $title;
}
private function addRelatedReports($relatedReports)
@@ -1148,8 +1015,8 @@ class ViewDataTable
*/
public function render()
{
- $this->main();
- return $this->getView()->render();
+ $this->buildView();
+ return $this->view->render();
}
/**
@@ -1173,21 +1040,28 @@ class ViewDataTable
$this->viewProperties['show_goals'] = false;
}
- if (!PluginsManager::getInstance()->isPluginLoaded('Annotations')) {
- $this->viewProperties['hide_annotations_view'] = true;
- }
-
- if ($this->idSubtable) {
- $this->viewProperties['datatable_template'] = $this->viewProperties['subtable_template'];
- }
-
if ($this->viewProperties['filter_limit'] == 0) { // TODO: should be possible to set limit to 0 (for whatever reason)
$this->viewProperties['filter_limit'] = false;
}
}
- protected function buildView($visualization)
+ protected function buildView()
{
+ $visualization = new $this->visualizationClass($this);
+
+ try {
+ $this->loadDataTableFromAPI();
+ $this->postDataTableLoadedFromAPI();
+ } catch (NoAccessException $e) {
+ throw $e;
+ } catch (\Exception $e) {
+ Piwik::log("Failed to get data from API: " . $e->getMessage());
+
+ $this->loadingError = array('message' => $e->getMessage());
+ }
+
+ $this->overrideViewProperties();
+
$template = $this->viewProperties['datatable_template'];
$view = new View($template);
@@ -1196,7 +1070,8 @@ class ViewDataTable
}
$view->visualization = $visualization;
-
+ $view->visualizationCssClass = $this->getDefaultDataTableCssClass();
+
if (!$this->dataTable === null) {
$view->dataTable = null;
} else {
@@ -1208,8 +1083,12 @@ class ViewDataTable
$view->deleteReportsOlderThan = Piwik_GetOption('delete_reports_older_than');
}
$view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet();
- $view->properties = $this->viewProperties;
- return $view;
+ $view->clientSidePropertiesToSet = $this->getClientSidePropertiesToSet();
+ $view->properties = $this->viewProperties; // TODO: should be $this. need to move non-view properties from the class
+
+ $nonCoreVisualizations = DataTableVisualization::getNonCoreVisualizations();
+ $view->nonCoreVisualizations = DataTableVisualization::getVisualizationInfoFor($nonCoreVisualizations);
+ $this->view = $view;
}
public function getDefaultDataTableCssClass()
@@ -1217,22 +1096,24 @@ class ViewDataTable
return 'dataTableViz' . Piwik::getUnnamespacedClassName($this->visualizationClass);
}
- /**
- * Sets view properties if they have not been set already.
- */
- public function defaultPropertiesTo($defaultValues)
+ private function setViewProperties($values)
{
- // TODO: This approach won't work. Will require more typing, but allow there to be a static
- // getDefaultProperties function for visualizations. used by constructor.
- $defaultView = new ViewDataTable(); // bit of a hack...
-
- foreach ($defaultValues as $name => $value) {
- if (!array_key_exists($name, $this->viewProperties)
- || (isset($defaultView->viewProperties[$name])
- && $this->viewProperties[$name] === $defaultView->viewProperties[$name])
- ) {
- $this->viewProperties[$name] = $value;
- }
+ foreach ($values as $name => $value) {
+ $this->setViewProperty($name, $value);
}
}
+
+ private function setDefaultProperties()
+ {
+ // set core default properties
+ $this->setViewProperties(Properties::getDefaultPropertyValues());
+
+ // set visualization default properties
+ if ($this->visualizationClass === null) {
+ return;
+ }
+
+ $visualizationClass = $this->visualizationClass;
+ $this->setViewProperties($visualizationClass::getDefaultPropertyValues());
+ }
} \ No newline at end of file
diff --git a/core/ViewDataTable/Cloud.php b/core/ViewDataTable/Cloud.php
deleted file mode 100644
index 653cc8c022..0000000000
--- a/core/ViewDataTable/Cloud.php
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-/**
- * Piwik - Open source web analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- * @category Piwik
- * @package Piwik
- */
-namespace Piwik\ViewDataTable;
-
-use Exception;
-use Piwik\ViewDataTable;
-use Piwik\View;
-use Piwik;
-use Piwik\Visualization;
-
-/**
- * Reads the requested DataTable from the API, and prepares the data to give
- * to Cloud that will display the tag cloud (via the template _dataTable_cloud.twig).
- *
- * @package Piwik
- * @subpackage ViewDataTable
- */
-class Cloud extends ViewDataTable
-{
- /**
- * @param bool $bool
- */
- public function setDisplayLogoInTagCloud($bool)
- {
- $this->viewProperties['display_logo_instead_of_label'] = $bool;
- }
-
- /**
- * Returns the dataTable id for the view
- *
- * @return string
- */
- protected function getViewDataTableId()
- {
- return 'cloud';
- }
-
- public function __construct()
- {
- parent::__construct();
-
- $this->viewProperties['show_offset_information'] = false;
- $this->viewProperties['show_exclude_low_population'] = false;
- $this->viewProperties['display_logo_instead_of_label'] = false;
- }
-
- /**
- * @see ViewDataTable::main()
- *
- * @return null
- */
- public function main()
- {
- if ($this->mainAlreadyExecuted) {
- return;
- }
- $this->mainAlreadyExecuted = true;
-
- try {
- $this->loadDataTableFromAPI();
- } catch (Exception $e) {
- Piwik\Piwik::log("Failed to get data from API: " . $e->getMessage());
-
- $this->loadingError = array('message' => $e->getMessage());
- }
-
- $this->checkStandardDataTable();
- $this->postDataTableLoadedFromAPI();
-
- $visualization = new Visualization\Cloud();
- $this->view = $this->buildView($visualization);
- }
-}
diff --git a/core/ViewDataTable/Properties.php b/core/ViewDataTable/Properties.php
index 8d55d5a926..457069dbb7 100644
--- a/core/ViewDataTable/Properties.php
+++ b/core/ViewDataTable/Properties.php
@@ -13,18 +13,29 @@ namespace Piwik\ViewDataTable;
use Exception;
use ReflectionClass;
+use Piwik\Piwik;
+use Piwik\Config;
+use Piwik\Metrics;
+use Piwik\Common;
/**
* Contains the list of all core DataTable display properties for use with ViewDataTable.
*
* @see ViewDataTable - for more info.
*
- * TODO: change the names of properties to match the const names where appropriate.
* TODO: list default value for each property
*/
class Properties
{
/**
+ * The default viewDataTable ID to use when determining which visualization to use.
+ * This property is only valid for reports whose properties are determined by the
+ * ViewDataTable.getReportDisplayProperties event. When manually creating ViewDataTables,
+ * setting this property will have no effect.
+ */
+ const DEFAULT_VIEW_TYPE = 'default_view_type';
+
+ /**
* This property determines which Twig template to use when rendering a ViewDataTable.
*
* TODO: shouldn't have this property. should only use visualization classes.
@@ -54,13 +65,6 @@ class Properties
const SORT_ORDER = 'filter_sort_order';
/**
- * The limit used when rendering a jqPlot graph.
- *
- * TODO: either replace w/ filter_limit, or make it a visualization property.
- */
- const GRAPH_LIMIT = 'graph_limit';
-
- /**
* The number of items to truncate the data set to before rendering the DataTable view.
*/
const LIMIT = 'filter_limit';
@@ -118,23 +122,13 @@ class Properties
const SHOW_ACTIVE_VIEW_ICON = 'show_active_view_icon';
/**
- * TODO: this property is specific ONLY to the row evolution popup. Need to move it.
- */
- const EXTERNAL_SERIES_TOGGLE = 'external_series_toggle';
-
- /**
- * TODO: this property is specific ONLY to the row evolution popup. Need to move it.
- */
- const EXTERNAL_SERIES_TOGGLE_SHOW_ALL = 'external_series_toggle_show_all';
-
- /**
* Related reports are listed below a datatable view. When clicked, the original report will
* change to the clicked report and the list will change so the original report can be
* navigated back to.
*
* @see also self::TITLE. Both must be set if associating related reports.
*/
- const RELATED_REPORTS = 'relatedReports';
+ const RELATED_REPORTS = 'related_reports';
/**
* The report title. Used with related reports so report headings can be changed when switching
@@ -150,13 +144,6 @@ class Properties
const SHOW_RELATED_REPORTS = 'show_related_reports';
/**
- * Array property that contains the names of columns that can be selected in the Series Picker.
- *
- * TODO: this is only applicable to graph views. move this.
- */
- const SELECTABLE_COLUMNS = 'selectable_columns';
-
- /**
* Contains the documentation for a report.
*/
const REPORT_DOCUMENTATION = 'documentation';
@@ -196,7 +183,7 @@ class Properties
*
* @see self::ROW_PICKER_VISIBLE_VALUES
*/
- const ROW_PICKER_VALUE_COLUMN = 'row_picker_mach_rows_by';
+ const ROW_PICKER_VALUE_COLUMN = 'row_picker_match_rows_by';
/**
* Contains the list of values available for the Row Picker.
@@ -228,13 +215,6 @@ class Properties
const ALWAYS_SHOW_LIMIT_DROPDOWN = 'show_limit_control';
/**
- * Controls whether offset information (ie, '5-10 of 20') is shown under the datatable.
- *
- * @see TODO
- */
- const SHOW_OFFSET_INFORMATION = 'show_offset_information';
-
- /**
* Controls whether the search box under the datatable is shown.
*/
const SHOW_SEARCH_BOX = 'show_search';
@@ -245,12 +225,6 @@ class Properties
const ENABLE_SORT = 'enable_sort';
/**
- * Controls whether annotations are shown or not.
- * TODO: This is only appropriate for evolution graphs. Move it.
- */
- const HIDE_ANNOTATIONS_VIEW = 'hide_annotations_view';
-
- /**
* Controls whether the footer icon that allows users to view data as a bar chart is shown.
*/
const SHOW_BAR_CHART_ICON = 'show_bar_chart';
@@ -339,18 +313,8 @@ class Properties
const VISUALIZATION_PROPERTIES = 'visualization_properties';
/**
- * Custom template used if displaying a subtable.
- *
- * TODO: This is specific to HtmlTable and should be replaced w/ allowing custom visualization for
- * subtables. Should not directly touch template.
- */
- const SUBTABLE_TEMPLATE = 'subtable_template';
-
- /**
- * CSS class to use in the output HTML div.
- *
- * TODO: This only changes based on the visualization type. Would be good if it didn't need to be
- * set at all...
+ * CSS class to use in the output HTML div. This is added in addition to the visualization CSS
+ * class.
*/
const DATATABLE_CSS_CLASS = 'datatable_css_class';
@@ -361,14 +325,6 @@ class Properties
const DATATABLE_JS_TYPE = 'datatable_js_type';
/**
- * Controls whether the entire DataTable should be rendered (including subtables) or just one
- * specific table in the tree.
- *
- * TODO: specific to htmltable. make a visualization property.
- */
- const SHOW_EXPANDED = 'show_expanded';
-
- /**
* If true, searching through the DataTable will search through all subtables.
*
* @see also self::FILTER_PATTERN
@@ -376,81 +332,16 @@ class Properties
const DO_RECURSIVE_SEARCH = 'search_recursive';
/**
- * Controls whether the row evolution DataTable Row Action icon is shown or not.
- *
- * TODO: specific to HtmlTable. move.
- *
- * @see also self::DISABLE_ROW_ACTIONS
- */
- const DISABLE_ROW_EVOLUTION = 'disable_row_evolution';
-
- /**
- * Controls whether any DataTable Row Action icons are shown. If true, no icons are shown.
- *
- * TODO: specific to HtmlTable. move.
- *
- * @see also self::DISABLE_ROW_EVOLUTION
- */
- const DISABLE_ROW_ACTIONS = 'disable_row_actions';
-
- /**
* The unit of the displayed column. Valid if only one non-label column is displayed.
*/
const DISPLAYED_COLUMN_UNIT = 'y_axis_unit';
/**
- * Controls whether the percentage of the total is displayed as a tooltip in Jqplot graphs.
- *
- * NOTE: Sometimes this percentage is meaningless (when the total of the column values is
- * not the total number of elements in the set). In this case the tooltip should not be
- * displayed.
- *
- * TODO: only valid for graphs... move it.
- */
- const DISPLAY_PERCENTAGE_IN_TOOLTIP = 'display_percentage_in_tooltip';
-
- /**
* Controls whether to show the 'Export as Image' footer icon.
*/
const SHOW_EXPORT_AS_IMAGE_ICON = 'show_export_as_image_icon';
/**
- * Controls whether all ticks & labels are shown on a graph's x-axis or just some.
- *
- * TODO: only for jqplot graphs.
- */
- const SHOW_ALL_TICKS = 'show_all_ticks';
-
- /**
- * If true, a row with totals of each DataTable column is added.
- *
- * TODO: only for jqplot graphs. also doesn't seem necessary w/ AddSummaryRow
- */
- const ADD_TOTAL_ROW = 'add_total_row';
-
- /**
- * If true, the 'label', 'nb_visits', 'nb_uniq_visitors' (if present), 'nb_actions',
- * 'nb_actions_per_visit', 'avg_time_on_site', 'bounce_rate' and 'conversion_rate' (if
- * goals view is not allowed) are displayed.
- *
- * TODO: HtmlTable property, only. Move.
- */
- const SHOW_EXTRA_COLUMNS = 'show_extra_columns';
-
- /**
- * If true, conversions for each existing goal will be displayed for the visits in
- * each row.
- *
- * TODO: HtmlTable property, only. Move.
- */
- const SHOW_GOALS_COLUMNS = 'show_goals_columns';
-
- /**
- * TODO: HtmlTable property, only. Move.
- */
- const DISABLE_SUBTABLE_IN_GOALS_VIEW = 'disable_subtable_when_show_goals';
-
- /**
* Array of DataTable filters that should be run before displaying a DataTable. Elements
* of this array can either be a closure or an array with at most three elements, including:
* - the filter name (or a closure)
@@ -470,23 +361,38 @@ class Properties
const SUBTABLE_CONTROLLER_ACTION = 'subtable_controller_action';
/**
- * TODO: specific only to jqplot graphs. change to just 'width'?
+ * Controls whether the 'prev'/'next' links are shown in the DataTable footer. These links
+ * change the 'filter_offset' query parameter, thus allowing pagination.
+ *
+ * @see self::SHOW_OFFSET_INFORMATION
+ */
+ const SHOW_PAGINATION_CONTROL = 'show_pagination_control';
+
+ /**
+ * Controls whether offset information (ie, '5-10 of 20') is shown under the datatable.
+ *
+ * @see self::SHOW_PAGINATION_CONTROL
+ */
+ const SHOW_OFFSET_INFORMATION = 'show_offset_information';
+
+ /**
+ * Controls whether annotations are shown or not.
*/
- const GRAPH_WIDTH = 'graph_width';
+ const HIDE_ANNOTATIONS_VIEW = 'hide_annotations_view';
/**
- * TODO: same as GRAPH_WIDTH
+ * The filter_limit query parameter value to use in export links.
*/
- const GRAPH_HEIGHT = 'graph_height';
+ const EXPORT_LIMIT = 'export_limit';
/**
- * TODO: only valid for jqplot graphs.
+ * Controls whether non-Core DataTable visualizations are shown or not.
*/
- const SHOW_SERIES_PICKER = 'show_series_picker';
+ const SHOW_NON_CORE_VISUALIZATIONS = 'show_non_core_visualizations';
/**
* Returns the set of all valid ViewDataTable properties. The result is an array with property
- * name as a key. Values of the array are undefined.
+ * names as keys. Values of the array are undefined.
*
* @return array
*/
@@ -495,14 +401,53 @@ class Properties
static $propertiesCache = null;
if ($propertiesCache === null) {
- $klass = new ReflectionClass(__CLASS__);
- $propertiesCache = array_flip($klass->getConstants());
+ $propertiesCache = self::getFlippedClassConstantMap(__CLASS__);
}
return $propertiesCache;
}
/**
+ * Returns the set of all valid properties for the given visualization class. The result is an
+ * array with property names as keys. Values of the array are undefined.
+ *
+ * @return array
+ */
+ public static function getVisualizationProperties($visualizationClass)
+ {
+ static $propertiesCache = array();
+
+ if ($visualizationClass === null) {
+ return array();
+ }
+
+ if (!isset($propertiesCache[$visualizationClass])) {
+ $properties = self::getFlippedClassConstantMap($visualizationClass);
+
+ $parentClass = get_parent_class($visualizationClass);
+ if ($parentClass != 'Piwik\\DataTableVisualization') {
+ $properties += self::getVisualizationProperties($parentClass);
+ }
+
+ $propertiesCache[$visualizationClass] = $properties;
+ }
+
+ return $propertiesCache[$visualizationClass];
+ }
+
+ /**
+ * Returns true if $name is a core ViewDataTable property, false if not.
+ *
+ * @param string $name
+ * @return bool
+ */
+ public static function isCoreViewProperty($name)
+ {
+ $properties = self::getAllProperties();
+ return isset($properties[$name]);
+ }
+
+ /**
* Checks if a property is a valid ViewDataTable property, and if not, throws an exception.
*
* @param string $name The property name.
@@ -510,10 +455,101 @@ class Properties
*/
public static function checkValidPropertyName($name)
{
- $properties = self::getAllProperties();
+ if (!self::isCoreViewProperty($name)) {
+ throw new Exception("Invalid ViewDataTable display property '$name'.");
+ }
+ }
+
+ /**
+ * Checks if a property is a valid visualization property for the given visualization class,
+ * and if not, throws an exception.
+ *
+ * @param string $visualizationClass
+ * @param string $name The property name.
+ * @throws Exception
+ */
+ public static function checkValidVisualizationProperty($visualizationClass, $name)
+ {
+ $properties = self::getVisualizationProperties($visualizationClass);
if (!isset($properties[$name])) {
- throw new Exception("Invalid ViewDataTable display property '$name'. Is this a visualization property? "
- . "If so, set it with \$view->visualization_properties->$name = ...");
+ throw new Exception("Invalid Visualization display property '$name' for '$visualizationClass'.");
}
}
+
+ /**
+ * Returns the default values for each core view property.
+ *
+ * @return array
+ */
+ public static function getDefaultPropertyValues()
+ {
+ $result = array(
+ 'datatable_template' => '@CoreHome/_dataTable',
+ 'show_goals' => false,
+ 'show_ecommerce' => false,
+ 'show_search' => true,
+ 'show_table' => true,
+ 'show_table_all_columns' => true,
+ 'show_all_views_icons' => true,
+ 'show_active_view_icon' => true,
+ 'show_bar_chart' => true,
+ 'show_pie_chart' => true,
+ 'show_tag_cloud' => true,
+ 'show_export_as_image_icon' => false,
+ 'show_export_as_rss_feed' => true,
+ 'show_exclude_low_population' => true,
+ 'show_offset_information' => true,
+ 'show_pagination_control' => true,
+ 'show_limit_control' => false,
+ 'show_footer' => true,
+ 'show_related_reports' => true,
+ 'show_non_core_visualizations' => true,
+ 'export_limit' => Config::getInstance()->General['API_datatable_default_limit'],
+ 'highlight_summary_row' => false,
+ 'related_reports' => array(),
+ 'title' => 'unknown',
+ 'tooltip_metadata_name' => false,
+ 'enable_sort' => true,
+ 'disable_generic_filters' => false,
+ 'disable_queued_filters' => false,
+ 'keep_summary_row' => false,
+ 'filter_excludelowpop' => false,
+ 'filter_excludelowpop_value' => false,
+ 'filter_pattern' => false,
+ 'filter_column' => false,
+ 'filter_limit' => false,
+ 'filter_sort_column' => false,
+ 'filter_sort_order' => false,
+ 'custom_parameters' => array(),
+ 'translations' => array_merge(
+ Metrics::getDefaultMetrics(),
+ Metrics::getDefaultProcessedMetrics()
+ ),
+ 'request_parameters_to_modify' => array(),
+ 'documentation' => false,
+ 'subtable_controller_action' => false,
+ 'datatable_css_class' => false,
+ 'filters' => array(),
+ 'hide_annotations_view' => true,
+ 'columns_to_display' => array(),
+ );
+
+ $columns = Common::getRequestVar('columns', false);
+ if ($columns !== false) {
+ $result['columns_to_display'] = Piwik::getArrayFromApiParameter($columns);
+ array_unshift($result['columns_to_display'], 'label');
+ }
+
+ return $result;
+ }
+
+ private static function getFlippedClassConstantMap($klass)
+ {
+ $klass = new ReflectionClass($klass);
+ $constants = $klass->getConstants();
+ unset($constants['ID']);
+ unset($constants['FOOTER_ICON']);
+ unset($constants['FOOTER_ICON_TITLE']);
+ return array_flip($constants);
+ }
} \ No newline at end of file
diff --git a/core/ViewDataTable/Sparkline.php b/core/ViewDataTable/Sparkline.php
index 02988a86dd..c3fd116114 100644
--- a/core/ViewDataTable/Sparkline.php
+++ b/core/ViewDataTable/Sparkline.php
@@ -28,7 +28,7 @@ class Sparkline extends ViewDataTable
*
* @return string
*/
- protected function getViewDataTableId()
+ public function getViewDataTableId()
{
return 'sparkline';
}
@@ -37,13 +37,8 @@ class Sparkline extends ViewDataTable
* @see ViewDataTable::main()
* @return mixed
*/
- public function main()
+ protected function buildView()
{
- if ($this->mainAlreadyExecuted) {
- return;
- }
- $this->mainAlreadyExecuted = true;
-
// If period=range, we force the sparkline to draw daily data points
$period = Common::getRequestVar('period');
if ($period == 'range') {
diff --git a/core/ViewDataTable/VisualizationPropertiesProxy.php b/core/ViewDataTable/VisualizationPropertiesProxy.php
index c6ef3f079e..6a39bf8a4d 100644
--- a/core/ViewDataTable/VisualizationPropertiesProxy.php
+++ b/core/ViewDataTable/VisualizationPropertiesProxy.php
@@ -20,11 +20,11 @@ use Piwik\ViewDataTable\Properties;
class VisualizationPropertiesProxy
{
/**
- * The visualization instance.
+ * The visualization class name.
*
- * @var array
+ * @var string
*/
- private $visualization;
+ private $visualizationClass;
/**
* Stores visualization properties.
@@ -36,11 +36,19 @@ class VisualizationPropertiesProxy
/**
* Constructor.
*
- * @param \Piwik\Visualization\ $visualization The visualization to get/set properties of.
+ * @param string $visualizationClass The visualization class to get/set properties of.
+ */
+ public function __construct($visualizationClass)
+ {
+ $this->visualizationClass = $visualizationClass;
+ }
+
+ /**
+ * Hack to allow property access in Twig (w/ property name checking).
*/
- public function __construct($visualization)
+ public function __call($name, $arguments)
{
- $this->visualization = $visualization;
+ return $this->$name;
}
/**
@@ -52,7 +60,10 @@ class VisualizationPropertiesProxy
*/
public function &__get($name)
{
- Properties::checkValidVisualizationProperty($this->visualization, $name);
+ if ($this->visualizationClass !== null) {
+ Properties::checkValidVisualizationProperty($this->visualizationClass, $name);
+ }
+
return $this->visualizationProperties[$name];
}
@@ -66,7 +77,10 @@ class VisualizationPropertiesProxy
*/
public function __set($name, $value)
{
- Properties::checkValidVisualizationProperty($this->visualization, $name);
+ if ($this->visualizationClass !== null) {
+ Properties::checkValidVisualizationProperty($this->visualizationClass, $name);
+ }
+
return $this->visualizationProperties[$name] = $value;
}
} \ No newline at end of file
diff --git a/core/Visualization/Cloud.php b/core/Visualization/Cloud.php
deleted file mode 100644
index 1a12f4061c..0000000000
--- a/core/Visualization/Cloud.php
+++ /dev/null
@@ -1,161 +0,0 @@
-<?php
-/**
- * Piwik - Open source web analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- * @category Piwik
- * @package Piwik
- */
-namespace Piwik\Visualization;
-
-use Piwik\Common;
-use Piwik\View;
-use Piwik\DataTable;
-use Piwik\DataTableVisualization;
-
-/**
- * Generates a tag cloud from a given data array.
- * The generated tag cloud can be in PHP format, or in HTML.
- *
- * Inspired from Derek Harvey (www.derekharvey.co.uk)
- *
- * @package Piwik
- * @subpackage Piwik_Visualization
- */
-class Cloud extends DataTableVisualization
-{
- /** Used by integration tests to make sure output is consistent. */
- public static $debugDisableShuffle = false;
-
- protected $wordsArray = array();
- public $truncatingLimit = 50;
-
- /**
- * Assign word to array
- * @param string $word
- * @param int $value
- * @return string
- */
- public function addWord($word, $value = 1)
- {
- if (isset($this->wordsArray[$word])) {
- $this->wordsArray[$word] += $value;
- } else {
- $this->wordsArray[$word] = $value;
- }
- }
-
- /**
- * Renders this visualization.
- *
- * @param DataTable $dataTable
- * @param array $properties
- * @return string
- */
- public function render($dataTable, $properties)
- {
- $view = new View("@CoreHome/_dataTableViz_tagCloud.twig");
- $view->properties = $properties;
-
- $columnToDisplay = $properties['columns_to_display'][1];
-
- $labelMetadata = array();
- foreach ($dataTable->getRows() as $row) {
- $logo = false;
- if ($properties['display_logo_instead_of_label']) {
- $logo = $row->getMetadata('logo');
- }
-
- $label = $row->getColumn('label');
-
- $labelMetadata[$label] = array(
- 'logo' => $logo,
- 'url' => $row->getMetadata('url'),
- );
-
- $this->addWord($label, $row->getColumn($columnToDisplay));
- }
- $cloudValues = $this->getCloudValues();
- foreach ($cloudValues as &$value) {
- $value['logoWidth'] = round(max(16, $value['percent']));
- }
- $view->labelMetadata = $labelMetadata;
- $view->cloudValues = $cloudValues;
-
- return $view->render();
- }
-
- private function getCloudValues()
- {
- $this->shuffleCloud();
- $return = array();
- if (empty($this->wordsArray)) {
- return array();
- }
- $maxValue = max($this->wordsArray);
- foreach ($this->wordsArray as $word => $popularity) {
- $wordTruncated = $word;
- if (Common::mb_strlen($word) > $this->truncatingLimit) {
- $wordTruncated = Common::mb_substr($word, 0, $this->truncatingLimit - 3) . '...';
- }
-
- // case hideFutureHoursWhenToday=1 shows hours with no visits
- if ($maxValue == 0) {
- $percent = 0;
- } else {
- $percent = ($popularity / $maxValue) * 100;
- }
- // CSS style value
- $sizeRange = $this->getClassFromPercent($percent);
-
- $return[$word] = array(
- 'word' => $word,
- 'wordTruncated' => $wordTruncated,
- 'value' => $popularity,
- 'size' => $sizeRange,
- 'percent' => $percent,
- );
- }
- return $return;
- }
-
- /**
- * Shuffle associated names in array
- */
- protected function shuffleCloud()
- {
- if (self::$debugDisableShuffle) {
- return;
- }
-
- $keys = array_keys($this->wordsArray);
-
- shuffle($keys);
-
- if (count($keys) && is_array($keys)) {
- $tmpArray = $this->wordsArray;
- $this->wordsArray = array();
- foreach ($keys as $key => $value)
- $this->wordsArray[$value] = $tmpArray[$value];
- }
- }
-
- /**
- * Get the class range using a percentage
- *
- * @param $percent
- *
- * @return int class
- */
- protected function getClassFromPercent($percent)
- {
- $mapping = array(95, 70, 50, 30, 15, 5, 0);
- foreach ($mapping as $key => $value) {
- if ($percent >= $value) {
- return $key;
- }
- }
- }
-}
diff --git a/core/Visualization/HtmlTable.php b/core/Visualization/HtmlTable.php
deleted file mode 100644
index 04519ae922..0000000000
--- a/core/Visualization/HtmlTable.php
+++ /dev/null
@@ -1,340 +0,0 @@
-<?php
-/**
- * Piwik - Open source web analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- * @category Piwik
- * @package Piwik
- */
-
-namespace Piwik\Visualization;
-
-use Piwik\Piwik;
-use Piwik\DataTable;
-use Piwik\View;
-use Piwik\Config;
-use Piwik\Common;
-use Piwik\Site;
-use Piwik\DataTableVisualization;
-use Piwik\DataTable\Filter\AddColumnsProcessedMetricsGoal;
-use \Piwik_Goals_API;
-
-/**
- * DataTable visualization that shows DataTable data in an HTML table.
- */
-class HtmlTable extends DataTableVisualization
-{
- const ID = 'table';
-
- // TODO: names for these types of properties are inappropriate. JS properties are actually properties
- // that get passed for each request. Overridable properties are properties that do not get passed on,
- // but are visible to client side JS. (change to clientSideProperties & clientSideParameters)
- static public $javaScriptProperties = array(
- 'search_recursive'
- );
-
- static public $overridableProperties = array(
- 'show_extra_columns',
- 'show_goals_columns'
- );
-
- /**
- * Constructor.
- */
- public function __construct($view)
- {
- if ($view->show_extra_columns) {
- $this->setShowExtraColumnsProperties($view);
- }
-
- if ($view->show_goals_columns) {
- $this->setShowGoalsColumnsProperties($view);
- }
-
- $view->defaultPropertiesTo($this->getDefaultPropertyValues());
- }
-
- /**
- * Renders this visualization.
- *
- * @param DataTable $dataTable
- * @param array $properties View Properties.
- * @return string
- */
- public function render($dataTable, $properties) // TODO: $properties should be a viewdatatable, I think.
- {
- $view = new View("@CoreHome/_dataTableViz_htmlTable.twig");
- $view->properties = $properties;
- $view->dataTable = $dataTable;
- return $view->render();
- }
-
- public static function getViewDataTableId($view) // TODO: shouldn't need this override
- {
- if ($view->show_extra_columns) {
- return 'tableAllColumns';
- } else if ($view->show_goals_columns) {
- return 'tableGoals';
- } else {
- return self::ID;
- }
- }
-
- private function getDefaultPropertyValues()
- {
- $defaults = array(
- 'enable_sort' => true,
- 'disable_row_evolution' => false,
- 'disable_row_actions' => false,
- 'subtable_template' => "@CoreHome/_dataTable.twig",
- 'datatable_js_type' => 'dataTable',
- 'filter_limit' => Config::getInstance()->General['datatable_default_limit'],
- 'show_extra_columns' => false,
- 'disable_subtable_when_show_goals' => false
- );
-
- if (Common::getRequestVar('enable_filter_excludelowpop', false) == '1') {
- $defaults['filter_excludelowpop'] = 'nb_visits';
- $defaults['filter_excludelowpop_value'] = null;
- }
-
- return $defaults;
- }
-
- private function setShowExtraColumnsProperties($view)
- {
- $view->filters[] = array('AddColumnsProcessedMetrics', array(), $priority = true);
-
- $view->filters[] = function ($dataTable, $view) {
- $columnsToDisplay = array('label', 'nb_visits');
-
- if (in_array('nb_uniq_visitors', $dataTable->getColumns())) {
- $columnsToDisplay[] = 'nb_uniq_visitors';
- }
-
- $columnsToDisplay = array_merge(
- $columnsToDisplay, array('nb_actions', 'nb_actions_per_visit', 'avg_time_on_site', 'bounce_rate')
- );
-
- // only display conversion rate for the plugins that do not provide "per goal" metrics
- // otherwise, conversion rate is meaningless as a whole (since we don't process 'cross goals' conversions)
- if (!$view->show_goals) {
- $columnsToDisplay[] = 'conversion_rate';
- }
-
- $view->columns_to_display = $columnsToDisplay;
- };
-
- $prettifyTime = array('\Piwik\Piwik', 'getPrettyTimeFromSeconds');
- $view->filters[] = array('ColumnCallbackReplace', array('avg_time_on_site', $prettifyTime));
-
- $view->show_exclude_low_population = true;
-
- $view->datatable_css_class = 'dataTableVizAllColumns';
- }
-
- private function setShowGoalsColumnsProperties($view)
- {
- $view->datatable_css_class = 'dataTableVizGoals';
- $view->show_exclude_low_population = true;
- $view->show_goals = true;
- $view->translations += array(
- 'nb_conversions' => Piwik_Translate('Goals_ColumnConversions'),
- 'conversion_rate' => Piwik_Translate('General_ColumnConversionRate'),
- 'revenue' => Piwik_Translate('Goals_ColumnRevenue'),
- 'revenue_per_visit' => Piwik_Translate('General_ColumnValuePerVisit'),
- );
- $view->metrics_documentation['nb_visits'] = Piwik_Translate('Goals_ColumnVisits');
-
- if (Common::getRequestVar('documentationForGoalsPage', 0, 'int') == 1) { // TODO: should not use query parameter
- $view->documentation = Piwik_Translate('Goals_ConversionByTypeReportDocumentation',
- array('<br />', '<br />', '<a href="http://piwik.org/docs/tracking-goals-web-analytics/" target="_blank">', '</a>'));
- }
-
- if (!$view->disable_subtable_when_show_goals) {
- $view->subtable_controller_action = null;
- }
-
- // set view properties based on goal requested
- $idSite = Common::getRequestVar('idSite', null, 'int');
- $idGoal = Common::getRequestVar('idGoal', AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW, 'string');
- if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
- $this->setPropertiesForEcommerceView($view);
- } else if ($idGoal == AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE) {
- $this->setPropertiesForGoals($view, $idSite, 'all');
- } else if ($idGoal == AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW) {
- $this->setPropertiesForGoalsOverview($view, $idSite);
- } else {
- $this->setPropertiesForGoals($view, $idSite, array($idGoal));
- }
-
- // add goals columns
- $view->filters[] = array(
- 'AddColumnsProcessedMetricsGoal', array($ignore = true, $idGoal), $priority = true);
-
- // prettify columns
- $setRatePercent = function ($rate, $thang = false) { return $rate == 0 ? "0%" : $rate; };
- foreach ($view->columns_to_display as $columnName) {
- if (strpos($columnName, 'conversion_rate') !== false) {
- $view->filters[] = array('ColumnCallbackReplace', array($columnName, $setRatePercent));
- }
- }
-
- $formatPercent = function ($value) use($idSite) {
- return Piwik::getPrettyMoney(sprintf("%.1f", $value), $idSite);
- };
-
- foreach ($view->columns_to_display as $columnName) {
- if ($this->isRevenueColumn($columnName)) {
- $view->filters[] = array('ColumnCallbackReplace', array($columnName, $formatPercent));
- }
- }
-
- // this ensures that the value is set to zero for all rows where the value was not set (no conversion)
- $identityFunction = function ($value) { return $value; };
- foreach ($view->columns_to_display as $columnName) {
- if (!$this->isRevenueColumn($columnName)) {
- $view->filters[] = array('ColumnCallbackReplace', array($columnName, $identityFunction));
- }
- }
- }
-
- private function setPropertiesForEcommerceView($view)
- {
- $view->filter_sort_column = 'goal_ecommerceOrder_revenue';
- $view->filter_sort_order = 'desc';
-
- $view->columns_to_display = array(
- 'label', 'nb_visits', 'goal_ecommerceOrder_nb_conversions', 'goal_ecommerceOrder_revenue',
- 'goal_ecommerceOrder_conversion_rate', 'goal_ecommerceOrder_avg_order_revenue', 'goal_ecommerceOrder_items',
- 'goal_ecommerceOrder_revenue_per_visit'
- );
-
- $view->translations += array(
- 'goal_ecommerceOrder_conversion_rate' => Piwik_Translate('Goals_ConversionRate', Piwik_Translate('Goals_EcommerceOrder')),
- 'goal_ecommerceOrder_nb_conversions' => Piwik_Translate('General_EcommerceOrders'),
- 'goal_ecommerceOrder_revenue' => Piwik_Translate('General_TotalRevenue'),
- 'goal_ecommerceOrder_revenue_per_visit' => Piwik_Translate('General_ColumnValuePerVisit'),
- 'goal_ecommerceOrder_avg_order_revenue' => Piwik_Translate('General_AverageOrderValue'),
- 'goal_ecommerceOrder_items' => Piwik_Translate('General_PurchasedProducts')
- );
-
- $goalName = Piwik_Translate('General_EcommerceOrders');
- $view->metrics_documentation += array(
- 'goal_ecommerceOrder_conversion_rate' => Piwik_Translate('Goals_ColumnConversionRateDocumentation', $goalName),
- 'goal_ecommerceOrder_nb_conversions' => Piwik_Translate('Goals_ColumnConversionsDocumentation', $goalName),
- 'goal_ecommerceOrder_revenue' => Piwik_Translate('Goals_ColumnRevenueDocumentation', $goalName),
- 'goal_ecommerceOrder_revenue_per_visit' => Piwik_Translate('Goals_ColumnAverageOrderRevenueDocumentation', $goalName),
- 'goal_ecommerceOrder_avg_order_revenue' => Piwik_Translate('Goals_ColumnAverageOrderRevenueDocumentation', $goalName),
- 'goal_ecommerceOrder_items' => Piwik_Translate('Goals_ColumnPurchasedProductsDocumentation', $goalName),
- 'revenue_per_visit' => Piwik_Translate('Goals_ColumnRevenuePerVisitDocumentation', $goalName)
- );
- }
-
- private function setPropertiesForGoalsOverview($view, $idSite)
- {
- $allGoals = $this->getGoals($idSite);
-
- // set view properties
- $view->columns_to_display = array('label', 'nb_visits');
-
- foreach ($allGoals as $goal) {
- $column = "goal_{$goal['idgoal']}_conversion_rate";
-
- $view->columns_to_display[] = $column;
- $view->translations[$column] = Piwik_Translate('Goals_ConversionRate', $goal['name']);
- $view->metrics_documentation[$column]
- = Piwik_Translate('Goals_ColumnConversionRateDocumentation', $goal['quoted_name'] ?: $goal['name']);
- }
-
- $view->columns_to_display[] = 'revenue_per_visit';
- $view->metrics_documentation['revenue_per_visit'] =
- Piwik_Translate('Goals_ColumnRevenuePerVisitDocumentation', Piwik_Translate('Goals_EcommerceAndGoalsMenu'));
- }
-
- private function setPropertiesForGoals($view, $idSite, $idGoals)
- {
- $allGoals = $this->getGoals($idSite);
-
- if ($idGoals == 'all') {
- $idGoals = array_keys($allGoals);
- } else {
- // only sort by a goal's conversions if not showing all goals (for FULL_REPORT)
- $view->filter_sort_column = 'goal_' . reset($idGoals) . '_nb_conversions';
- $view->filter_sort_order = 'desc';
- }
-
- $view->columns_to_display = array('label', 'nb_visits');
-
- $goalColumnTemplates = array(
- 'goal_%s_nb_conversions',
- 'goal_%s_conversion_rate',
- 'goal_%s_revenue',
- 'goal_%s_revenue_per_visit',
- );
-
- // set columns to display (columns of same type but different goals will be next to each other,
- // ie, goal_0_nb_conversions, goal_1_nb_conversions, etc.)
- foreach ($goalColumnTemplates as $idx => $columnTemplate) {
- foreach ($idGoals as $idGoal) {
- $column = sprintf($columnTemplate, $idGoal);
- $view->columns_to_display[] = $column;
- }
- }
-
- // set translations & metric docs for goal specific metrics
- foreach ($idGoals as $idGoal) {
- $goalName = $allGoals[$idGoal]['name'];
- $quotedGoalName = $allGoals[$idGoal]['quoted_name'] ?: $goalName;
-
- $view->translations += array(
- 'goal_' . $idGoal . '_nb_conversions' => Piwik_Translate('Goals_Conversions', $goalName),
- 'goal_' . $idGoal . '_conversion_rate' => Piwik_Translate('Goals_ConversionRate', $goalName),
- 'goal_' . $idGoal . '_revenue' =>
- Piwik_Translate('%s ' . Piwik_Translate('Goals_ColumnRevenue'), $goalName),
- 'goal_' . $idGoal . '_revenue_per_visit' =>
- Piwik_Translate('%s ' . Piwik_Translate('General_ColumnValuePerVisit'), $goalName),
- );
-
- $view->metrics_documentation += array(
- 'goal_' . $idGoal . '_nb_conversions' => Piwik_Translate('Goals_ColumnConversionsDocumentation', $quotedGoalName),
- 'goal_' . $idGoal . '_conversion_rate' => Piwik_Translate('Goals_ColumnConversionRateDocumentation', $quotedGoalName),
- 'goal_' . $idGoal . '_revenue' => Piwik_Translate('Goals_ColumnRevenueDocumentation', $quotedGoalName),
- 'goal_' . $idGoal . '_revenue_per_visit' =>
- Piwik_Translate('Goals_ColumnRevenuePerVisitDocumentation', Piwik_Translate('Goals_EcommerceAndGoalsMenu')),
- );
- }
-
- $view->columns_to_display[] = 'revenue_per_visit';
- }
-
- private function getGoals($idSite)
- {
- // get all goals to display info for
- $allGoals = array();
-
- if (Site::isEcommerceEnabledFor($idSite)) {
- $ecommerceGoal = array(
- 'idgoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER,
- 'name' => Piwik_Translate('Goals_EcommerceOrder'),
- 'quoted_name' => false
- );
- $allGoals[$ecommerceGoal['idgoal']] = $ecommerceGoal;
- }
-
- $siteGoals = Piwik_Goals_API::getInstance()->getGoals($idSite);
- foreach ($siteGoals as &$goal) {
- $goal['quoted_name'] = '"' . $goal['name'] . '"';
- $allGoals[$goal['idgoal']] = $goal;
- }
-
- return $allGoals;
- }
-
- private function isRevenueColumn($name)
- {
- return strpos($name, '_revenue') !== false || $name == 'revenue_per_visit';
- }
-} \ No newline at end of file
diff --git a/core/Visualization/JqplotGraph.php b/core/Visualization/JqplotGraph.php
deleted file mode 100644
index caa125f702..0000000000
--- a/core/Visualization/JqplotGraph.php
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-/**
- * Piwik - Open source web analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- * @category Piwik
- * @package Piwik
- */
-
-namespace Piwik\Visualization;
-
-use Piwik\Common;
-use Piwik\View;
-use Piwik\JqplotDataGenerator;
-use Piwik\DataTable;
-use Piwik\DataTableVisualization;
-
-/**
- * DataTable visualization that displays DataTable data in a JQPlot graph.
- * TODO: should merge all this logic w/ jqplotdatagenerator & 'Chart' visualizations.
- */
-class JqplotGraph extends DataTableVisualization
-{
- const DEFAULT_GRAPH_HEIGHT = 250;
-
- /**
- * TODO
- */
- public function __construct($view)
- {
- // Graphs require the full dataset, so no filters
- $this->request_parameters_to_modify['disable_generic_filters'] = true;
-
- // the queued filters will be manually applied later. This is to ensure that filtering using search
- // will be done on the table before the labels are enhanced (see ReplaceColumnNames)
- $this->request_parameters_to_modify['disable_queued_filters'] = true;
-
- $view->defaultPropertiesTo($this->getDefaultPropertyValues($view));
-
- if ($view->show_goals) {
- $goalMetrics = array('nb_conversions', 'revenue');
- $view->selectable_columns = array_merge($view->selectable_columns, $goalMetrics);
-
- $view->translations['nb_conversions'] = Piwik_Translate('Goals_ColumnConversions');
- $view->translations['revenue'] = Piwik_Translate('General_TotalRevenue');
- }
-
- $view->datatable_css_class = 'dataTableGraph'; // TODO: should be different css per visualization
- }
-
- /**
- * TODO
- */
- protected function getDefaultPropertyValues($view)
- {
- $result = array(
- 'show_offset_information' => false,
- 'show_pagination_control' => false,
- 'show_exclude_low_population' => false,
- 'show_search' => false,
- 'show_export_as_image_icon' => true,
- 'display_percentage_in_tooltip' => true,
- 'display_percentage_in_tooltip' => true,
- 'y_axis_unit' => '',
- 'show_all_ticks' => 0,
- 'add_total_row' => 0,
- 'allow_multi_select_series_picker' => true,
- 'row_picker_mach_rows_by' => false,
- 'row_picker_visible_rows' => array(),
- 'selectable_columns' => array(),
- 'graph_width' => '100%',
- 'graph_height' => self::DEFAULT_GRAPH_HEIGHT . 'px'
- );
-
- // do not sort if sorted column was initially "label" or eg. it would make "Visits by Server time" not pretty
- if ($view->filter_sort_column != 'label') {
- $columns = $view->columns_to_display;
-
- $firstColumn = reset($columns);
- if ($firstColumn == 'label') {
- $firstColumn = next($columns);
- }
-
- $result['filter_sort_column'] = $firstColumn;
- $result['filter_sort_order'] = 'desc';
- }
-
- // selectable columns
- $selectableColumns = array('nb_visits', 'nb_actions');
- if (Common::getRequestVar('period', false) == 'day') {
- $selectableColumns[] = 'nb_uniq_visitors';
- }
- $result['selectable_columns'] = $selectableColumns;
-
- return $result;
- }
-
- /**
- * Renders this visualization.
- *
- * @param DataTable $dataTable
- * @param array $properties View Properties.
- * @return string
- */
- public function render($dataTable, $properties)
- {
- $view = new View("@CoreHome/_dataTableViz_jqplotGraph.twig");
- $view->properties = $properties;
- $view->dataTable = $dataTable;
- $view->data = $this->getGraphData($dataTable, $properties);
- return $view->render();
- }
-
- /**
- * Generats JQPlot graph data for a DataTable.
- */
- private function getGraphData($dataTable, $properties)
- {
- $properties = array_merge($properties, $properties['request_parameters_to_modify']);
- $dataGenerator = $this->makeDataGenerator($properties);
-
- $jsonData = $dataGenerator->generate($dataTable);
- return str_replace(array("\r", "\n"), '', $jsonData);
- }
-
- protected function makeDataGenerator($properties)
- {
- return JqplotDataGenerator::factory($properties['graph_type'], $properties);
- }
-} \ No newline at end of file
diff --git a/core/Visualization/JqplotGraph/Bar.php b/core/Visualization/JqplotGraph/Bar.php
deleted file mode 100644
index 72de0beb8a..0000000000
--- a/core/Visualization/JqplotGraph/Bar.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-/**
- * Piwik - Open source web analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- * @category Piwik
- * @package Piwik
- */
-
-namespace Piwik\Visualization\JqplotGraph;
-
-use Piwik\Visualization\JqplotGraph;
-use Piwik\JqplotDataGenerator;
-
-/**
- * TODO
- */
-class Bar extends JqplotGraph
-{
- const ID = 'graphVerticalBar';
-
- protected function getDefaultPropertyValues($view)
- {
- $result = parent::getDefaultPropertyValues($view);
- $result['graph_limit'] = 6;
- return $result;
- }
-
- protected function makeDataGenerator($properties)
- {
- return JqplotDataGenerator::factory('bar', $properties);
- }
-} \ No newline at end of file
diff --git a/core/Visualization/JqplotGraph/Evolution.php b/core/Visualization/JqplotGraph/Evolution.php
deleted file mode 100644
index 5d7734061b..0000000000
--- a/core/Visualization/JqplotGraph/Evolution.php
+++ /dev/null
@@ -1,149 +0,0 @@
-<?php
-/**
- * Piwik - Open source web analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- * @category Piwik
- * @package Piwik
- */
-
-namespace Piwik\Visualization\JqplotGraph;
-
-use Piwik\Common;
-use Piwik\Site;
-use Piwik\Controller;
-use Piwik\Period\Range;
-use Piwik\Visualization\JqplotGraph;
-use Piwik\JqplotDataGenerator;
-
-/**
- * TODO
- */
-class Evolution extends JqplotGraph
-{
- const GRAPH_HEIGHT = 170;
- const ID = 'graphEvolution';
-
- public function __construct($view)
- {
- parent::__construct($view);
- $view->datatable_css_class = 'dataTableEvolutionGraph';
-
- // period will be overridden when 'range' is requested in the UI // TODO: this code probably shouldn't be here...
- // but the graph will display for each day of the range.
- // Default 'range' behavior is to return the 'sum' for the range
- if (Common::getRequestVar('period', false) == 'range') {
- $view->request_parameters_to_modify['period'] = 'day';
- }
-
- $this->calculateEvolutionDateRange($view);
- }
-
- protected function getDefaultPropertyValues($view)
- {
- $result = parent::getDefaultPropertyValues($view);
- $result['graph_height'] = self::GRAPH_HEIGHT . 'px';
- $result['show_all_views_icons'] = false;
- $result['show_table'] = false;
- $result['show_table'] = false;
- $result['show_table_all_columns'] = false;
- $result['hide_annotations_view'] = false;
- return $result;
- }
-
- protected function makeDataGenerator($properties)
- {
- return JqplotDataGenerator::factory('evolution', $properties);
- }
-
- /**
- * Based on the period, date and evolution_{$period}_last_n query parameters,
- * calculates the date range this evolution chart will display data for.
- */
- private function calculateEvolutionDateRange(&$view)
- {
- $period = Common::getRequestVar('period');
-
- $defaultLastN = self::getDefaultLastN($period);
- $originalDate = Common::getRequestVar('date', 'last' . $defaultLastN, 'string');
-
- if ($period != 'range') { // show evolution limit if the period is not a range
- $view->show_limit_control = true;
-
- // set the evolution_{$period}_last_n query param
- if (Range::parseDateRange($originalDate)) { // if a multiple period
- // overwrite last_n param using the date range
- $oPeriod = new Range($period, $originalDate);
- $lastN = count($oPeriod->getSubperiods());
- } else { // if not a multiple period
- list($newDate, $lastN) = self::getDateRangeAndLastN($period, $originalDate, $defaultLastN);
- $view->request_parameters_to_modify['date'] = $newDate;
- $view->custom_parameters['dateUsedInGraph'] = $newDate;
- }
- $lastNParamName = self::getLastNParamName($period);
- $view->custom_parameters[$lastNParamName] = $lastN;
- }
- }
-
- /**
- * Returns the entire date range and lastN value for the current request, based on
- * a period type and end date.
- *
- * @param string $period The period type, 'day', 'week', 'month' or 'year'
- * @param string $endDate The end date.
- * @param int|null $defaultLastN The default lastN to use. If null, the result of
- * getDefaultLastN is used.
- * @return array An array w/ two elements. The first is a whole date range and the second
- * is the lastN number used, ie, array('2010-01-01,2012-01-02', 2).
- */
- public static function getDateRangeAndLastN($period, $endDate, $defaultLastN = null)
- {
- if ($defaultLastN === null) {
- $defaultLastN = self::getDefaultLastN($period);
- }
-
- $lastNParamName = self::getLastNParamName($period);
- $lastN = Common::getRequestVar($lastNParamName, $defaultLastN, 'int');
-
- $site = new Site(Common::getRequestVar('idSite'));
-
- $dateRange = Controller::getDateRangeRelativeToEndDate($period, 'last' . $lastN, $endDate, $site);
-
- return array($dateRange, $lastN);
- }
-
- /**
- * Returns the default last N number of dates to display for a given period.
- *
- * @param string $period 'day', 'week', 'month' or 'year'
- * @return int
- */
- public static function getDefaultLastN($period)
- {
- switch ($period) {
- case 'week':
- return 26;
- case 'month':
- return 24;
- case 'year':
- return 5;
- case 'day':
- default:
- return 30;
- }
- }
-
- /**
- * Returns the query parameter that stores the lastN number of periods to get for
- * the evolution graph.
- *
- * @param string $period The period type, 'day', 'week', 'month' or 'year'.
- * @return string
- */
- public static function getLastNParamName($period)
- {
- return "evolution_{$period}_last_n";
- }
-} \ No newline at end of file
diff --git a/core/Visualization/JqplotGraph/Pie.php b/core/Visualization/JqplotGraph/Pie.php
deleted file mode 100644
index b7fbcf4b4d..0000000000
--- a/core/Visualization/JqplotGraph/Pie.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-/**
- * Piwik - Open source web analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- * @category Piwik
- * @package Piwik
- */
-
-namespace Piwik\Visualization\JqplotGraph;
-
-use Piwik\Visualization\JqplotGraph;
-use Piwik\JqplotDataGenerator;
-
-/**
- * TODO
- */
-class Pie extends JqplotGraph
-{
- const ID = 'graphPie';
-
- protected function getDefaultPropertyValues($view)
- {
- $result = parent::getDefaultPropertyValues($view);
- $result['graph_limit'] = 6;
- $result['allow_multi_select_series_picker'] = false;
- return $result;
- }
-
- protected function makeDataGenerator($properties)
- {
- return JqplotDataGenerator::factory('pie', $properties);
- }
-} \ No newline at end of file