diff options
author | Thomas ZILLIOX <thomas@zilliox.me> | 2013-08-08 03:37:12 +0400 |
---|---|---|
committer | Thomas ZILLIOX <thomas@zilliox.me> | 2013-08-08 03:37:12 +0400 |
commit | 6353a269ac30b5fe6b5da8ff7c97a2e14563661c (patch) | |
tree | f028ff54049c317e817e4c62ab5ae47458837de5 /core | |
parent | a440086526162d35f54fbe1f5f167256bd636e89 (diff) | |
parent | 07c430fba0014336fce6071dd8a4c1fb9c65ef25 (diff) |
Merge from piwik master
Diffstat (limited to 'core')
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 |