diff options
74 files changed, 1727 insertions, 1030 deletions
diff --git a/config/global.ini.php b/config/global.ini.php index 10d71efd31..1114efc681 100644 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -494,6 +494,7 @@ logger_file_path = tmp/logs Plugins[] = CorePluginsAdmin Plugins[] = CoreAdminHome Plugins[] = CoreHome +Plugins[] = CoreVisualizations Plugins[] = Proxy Plugins[] = API Plugins[] = Widgetize 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/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/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/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 diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php index 768389f8c0..538adccc8b 100644 --- a/plugins/Actions/API.php +++ b/plugins/Actions/API.php @@ -122,9 +122,11 @@ class Piwik_Actions_API * * @return DataTable|DataTable\Map */ - public function getPageUrls($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false) + public function getPageUrls($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false, + $depth = false) { - $dataTable = Archive::getDataTableFromArchive('Actions_actions_url', $idSite, $period, $date, $segment, $expanded, $idSubtable); + $dataTable = Archive::getDataTableFromArchive( + 'Actions_actions_url', $idSite, $period, $date, $segment, $expanded, $idSubtable, $depth); $this->filterPageDatatable($dataTable); $this->filterActionsDataTable($dataTable, $expanded); return $dataTable; diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php index 2d78cf9959..20b3985e14 100644 --- a/plugins/Actions/Actions.php +++ b/plugins/Actions/Actions.php @@ -60,9 +60,15 @@ class Piwik_Actions extends Plugin 'API.getReportMetadata' => 'getReportMetadata', 'API.getSegmentsMetadata' => 'getSegmentsMetadata', 'ViewDataTable.getReportDisplayProperties' => 'getReportDisplayProperties', + 'AssetManager.getCssFiles' => 'getCssFiles', ); return $hooks; } + + public function getCssFiles(&$cssFiles) + { + $cssFiles[] = "plugins/Actions/stylesheets/dataTableActions.less"; + } public function getSegmentsMetadata(&$segments) { @@ -647,9 +653,9 @@ class Piwik_Actions extends Plugin private function addBaseDisplayProperties(&$result) { - $result['datatable_css_class'] = 'dataTableActions'; $result['datatable_js_type'] = 'actionDataTable'; - $result['subtable_template'] = '@CoreHome/_dataTableActions_subDataTable.twig'; + $result['visualization_properties']['HtmlTable']['subtable_template'] = + '@CoreHome/_dataTableActions_subDataTable.twig'; $result['search_recursive'] = true; $result['show_all_views_icons'] = false; $result['show_table_all_columns'] = false; @@ -660,13 +666,19 @@ class Piwik_Actions extends Plugin $result['custom_parameters'] = array('flat' => 0); if (ViewDataTable::shouldLoadExpanded()) { - $result['show_expanded'] = true; + $result['visualization_properties']['HtmlTable']['show_expanded'] = true; $result['filters'][] = function ($dataTable) { Piwik_Actions::setDataTableRowLevels($dataTable); }; } - + + $result['filters'][] = function ($dataTable, $view) { + if ($view->getViewDataTableId() == 'table') { + $view->datatable_css_class = 'dataTableActions'; + } + }; + return $result; } @@ -780,7 +792,7 @@ class Piwik_Actions extends Plugin 'filter_sort_column' => 'entry_nb_visits', 'filter_sort_order' => 'desc', 'title' => Piwik_Translate('Actions_SubmenuPagesEntry'), - 'relatedReports' => array( + 'related_reports' => array( 'Actions.getEntryPageTitles' => Piwik_Translate('Actions_EntryPageTitles') ), 'self_url' => $reportUrl @@ -808,7 +820,7 @@ class Piwik_Actions extends Plugin 'filter_sort_column' => 'exit_nb_visits', 'filter_sort_order' => 'desc', 'title' => Piwik_Translate('Actions_SubmenuPagesExit'), - 'relatedReports' => array( + 'related_reports' => array( 'Actions.getExitPageTitles' => Piwik_Translate('Actions_ExitPageTitles') ), 'self_url' => $reportUrl, @@ -866,7 +878,11 @@ class Piwik_Actions extends Plugin 'columns_to_display' => array('label', 'nb_visits', 'nb_pages_per_search'), 'show_table_all_columns' => false, 'show_bar_chart' => false, - 'disable_row_evolution' => false, + 'visualization_properties' => array( + 'HtmlTable' => array( + 'disable_row_evolution' => false, + ) + ) ); } @@ -891,7 +907,7 @@ class Piwik_Actions extends Plugin 'filter_sort_order' => 'desc', 'show_exclude_low_population' => false, 'title' => $title, - 'relatedReports' => $relatedReports + 'related_reports' => $relatedReports ); $this->addExcludeLowPopDisplayProperties($result); @@ -914,7 +930,7 @@ class Piwik_Actions extends Plugin 'columns_to_display' => array('label', 'nb_hits', 'nb_visits', 'bounce_rate', 'avg_time_on_page', 'exit_rate', 'avg_time_generation'), 'title' => Piwik_Translate('Actions_SubmenuPageTitles'), - 'relatedReports' => array( + 'related_reports' => array( 'Actions.getEntryPageTitles' => Piwik_Translate('Actions_EntryPageTitles'), 'Actions.getExitPageTitles' => Piwik_Translate('Actions_ExitPageTitles'), ), @@ -940,7 +956,7 @@ class Piwik_Actions extends Plugin ), 'columns_to_display' => array('label', 'entry_nb_visits', 'entry_bounce_count', 'bounce_rate'), 'title' => Piwik_Translate('Actions_EntryPageTitles'), - 'relatedReports' => array( + 'related_reports' => array( 'Actions.getPageTitles' => Piwik_Translate('Actions_SubmenuPageTitles'), "Actions.$entryPageUrlAction" => Piwik_Translate('Actions_SubmenuPagesEntry') ), @@ -964,7 +980,7 @@ class Piwik_Actions extends Plugin ), 'columns_to_display' => array('label', 'exit_nb_visits', 'nb_visits', 'exit_rate'), 'title' => Piwik_Translate('Actions_ExitPageTitles'), - 'relatedReports' => array( + 'related_reports' => array( 'Actions.getPageTitles' => Piwik_Translate('Actions_SubmenuPageTitles'), "Actions.$exitPageUrlAction" => Piwik_Translate('Actions_SubmenuPagesExit'), ), diff --git a/plugins/Actions/stylesheets/dataTableActions.less b/plugins/Actions/stylesheets/dataTableActions.less new file mode 100644 index 0000000000..7c059d948b --- /dev/null +++ b/plugins/Actions/stylesheets/dataTableActions.less @@ -0,0 +1,4 @@ +.dataTableActions > .dataTableWrapper { + width: 500px; + min-height: 1px; +}
\ No newline at end of file diff --git a/plugins/Annotations/API.php b/plugins/Annotations/API.php index b1abd3c480..ec34c1f127 100755 --- a/plugins/Annotations/API.php +++ b/plugins/Annotations/API.php @@ -13,6 +13,8 @@ use Piwik\Period\Range; use Piwik\Piwik; use Piwik\Date; use Piwik\ViewDataTable; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution as EvolutionViz; + /** * @see plugins/Annotations/AnnotationList.php */ @@ -328,7 +330,7 @@ class Piwik_Annotations_API $endDate = $oPeriod->getDateEnd(); } else // if the range includes the last N periods { - list($date, $lastN) = \Piwik\Visualization\JqplotGraph\Evolution::getDateRangeAndLastN($period, $date, $lastN); + list($date, $lastN) = EvolutionViz::getDateRangeAndLastN($period, $date, $lastN); list($startDate, $endDate) = explode(',', $date); $startDate = Date::factory($startDate); diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php index c21c3168f7..1ffec819ff 100644 --- a/plugins/CoreHome/CoreHome.php +++ b/plugins/CoreHome/CoreHome.php @@ -47,9 +47,7 @@ class Piwik_CoreHome extends Plugin $cssFiles[] = "plugins/CoreHome/stylesheets/dataTable.less"; $cssFiles[] = "plugins/CoreHome/stylesheets/cloud.less"; $cssFiles[] = "plugins/CoreHome/stylesheets/jquery.ui.autocomplete.css"; - $cssFiles[] = "plugins/CoreHome/stylesheets/jqplot.css"; $cssFiles[] = "plugins/CoreHome/stylesheets/jqplotColors.less"; - $cssFiles[] = "plugins/CoreHome/stylesheets/sparklineColors.less"; $cssFiles[] = "plugins/CoreHome/stylesheets/promo.less"; $cssFiles[] = "plugins/CoreHome/stylesheets/color_manager.css"; } @@ -77,10 +75,8 @@ class Piwik_CoreHome extends Plugin $jsFiles[] = "plugins/CoreHome/javascripts/corehome.js"; $jsFiles[] = "plugins/CoreHome/javascripts/dataTable_manager.js"; $jsFiles[] = "plugins/CoreHome/javascripts/donate.js"; - $jsFiles[] = "plugins/CoreHome/javascripts/jqplot.js"; $jsFiles[] = "libs/jqplot/jqplot-custom.min.js"; $jsFiles[] = "plugins/CoreHome/javascripts/promo.js"; $jsFiles[] = "plugins/CoreHome/javascripts/color_manager.js"; } - -} +}
\ No newline at end of file diff --git a/plugins/CoreHome/DataTableRowAction/RowEvolution.php b/plugins/CoreHome/DataTableRowAction/RowEvolution.php index d60b54c4ab..46654c531e 100644 --- a/plugins/CoreHome/DataTableRowAction/RowEvolution.php +++ b/plugins/CoreHome/DataTableRowAction/RowEvolution.php @@ -16,6 +16,7 @@ use Piwik\Date; use Piwik\ViewDataTable; use Piwik\Url; use Piwik\Visualization\Chart\Evolution; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution as EvolutionViz; /** * ROW EVOLUTION @@ -97,8 +98,7 @@ class Piwik_CoreHome_DataTableRowAction_RowEvolution if ($this->period != 'range') { // handle day, week, month and year: display last X periods $end = $date->toString(); - list($this->date, $lastN) = - \Piwik\Visualization\JqplotGraph\Evolution::getDateRangeAndLastN($this->period, $end); + list($this->date, $lastN) = EvolutionViz::getDateRangeAndLastN($this->period, $end); } $this->segment = \Piwik\API\Request::getRawSegmentFromRequest(); @@ -186,25 +186,26 @@ class Piwik_CoreHome_DataTableRowAction_RowEvolution public function getRowEvolutionGraph($graphType = false, $metrics = false) { // set up the view data table - $view = ViewDataTable::factory( - $graphType ?: $this->graphType, $this->apiMethod, $controllerAction = 'CoreHome.getRowEvolutionGraph'); + $view = ViewDataTable::factory($graphType ?: $this->graphType, $this->apiMethod, + $controllerAction = 'CoreHome.getRowEvolutionGraph', $forceDefault = true); $view->setDataTable($this->dataTable); if (!empty($this->graphMetrics)) { // In row Evolution popover, this is empty $view->columns_to_display = array_keys($metrics ?: $this->graphMetrics); } + $view->show_goals = false; $view->show_all_views_icons = false; $view->show_active_view_icon = false; $view->show_related_reports = false; - $view->show_series_picker = false; + $view->visualization_properties->show_series_picker = false; foreach ($this->availableMetrics as $metric => $metadata) { $view->translations[$metric] = $metadata['name']; } - $view->external_series_toggle = 'RowEvolutionSeriesToggle'; - $view->external_series_toggle_show_all = $this->initiallyShowAllMetrics; + $view->visualization_properties->external_series_toggle = 'RowEvolutionSeriesToggle'; + $view->visualization_properties->external_series_toggle_show_all = $this->initiallyShowAllMetrics; return $view; } @@ -271,10 +272,9 @@ class Piwik_CoreHome_DataTableRowAction_RowEvolution { // sparkline is always echoed, so we need to buffer the output $view = $this->getRowEvolutionGraph($graphType = 'sparkline', $metrics = array($metric => $metric)); - $view->main(); ob_start(); - $view->getView()->render(); + $view->render(); $spark = ob_get_contents(); ob_end_clean(); diff --git a/plugins/CoreHome/javascripts/dataTable.js b/plugins/CoreHome/javascripts/dataTable.js index e269027e9d..a607e642c9 100644 --- a/plugins/CoreHome/javascripts/dataTable.js +++ b/plugins/CoreHome/javascripts/dataTable.js @@ -457,7 +457,7 @@ dataTable.prototype = var totalRows = Number(self.param.totalRows); var offsetEndDisp = offsetEnd; - if (self.param.keep_summary_row == 1) --totalRows; + if (self.props.keep_summary_row == 1) --totalRows; if (offsetEnd > totalRows) offsetEndDisp = totalRows; @@ -475,7 +475,7 @@ dataTable.prototype = var offsetEnd = Number(self.param.filter_offset) + Number(self.param.filter_limit); var totalRows = Number(self.param.totalRows); - if (self.param.keep_summary_row == 1) --totalRows; + if (self.props.keep_summary_row == 1) --totalRows; if (offsetEnd < totalRows) { $(this).css('display', 'inline'); } diff --git a/plugins/CoreHome/javascripts/dataTable_manager.js b/plugins/CoreHome/javascripts/dataTable_manager.js index a1ea816484..e2f81250ca 100644 --- a/plugins/CoreHome/javascripts/dataTable_manager.js +++ b/plugins/CoreHome/javascripts/dataTable_manager.js @@ -51,10 +51,8 @@ $('div.dataTable').each(function () { if (!$(this).attr('id')) { var params = JSON.parse($(this).attr('data-params') || '{}'); - var tableType = $(this).attr('data-table-type'); - if (!tableType) { - tableType = 'dataTable'; - } + var props = JSON.parse($(this).attr('data-props') || '{}'); + var tableType = $(this).attr('data-table-type') || 'dataTable'; // convert values in params that are arrays to comma separated string lists for (var key in params) { @@ -63,7 +61,7 @@ } } - self.initSingleDataTable(this, window[tableType], params); + self.initSingleDataTable(this, window[tableType], params, props); } }); }, @@ -74,8 +72,9 @@ * @param {Element} domElem The DataTable div element. * @param {Function} klass The DataTable's JS class. * @param {Object} params The request params used. + * @param {Object} props The view properties that should be visible to the JS. */ - initSingleDataTable: function (domElem, klass, params) { + initSingleDataTable: function (domElem, klass, params, props) { var newId = this.getNextId(); $(domElem).attr('id', newId); @@ -84,6 +83,7 @@ $(domElem).data('dataTableInstance', table); table.param = params; + table.props = props; table.init(newId); // if the datatable has a graph, init the graph diff --git a/plugins/CoreHome/stylesheets/dataTable/_dataTable.less b/plugins/CoreHome/stylesheets/dataTable/_dataTable.less index 909014db32..6083dd1cca 100644 --- a/plugins/CoreHome/stylesheets/dataTable/_dataTable.less +++ b/plugins/CoreHome/stylesheets/dataTable/_dataTable.less @@ -1,8 +1,3 @@ -/*Overriding some dataTable css for better dashboard display*/ -.widget .dataTableWrapper { - width: 100%; -} - /* main data table */ .dataTable { border: 0; @@ -145,28 +140,12 @@ table.dataTable img { margin-left: 0; } -/* container of each table */ -.dataTableVizHtmlTable > .dataTableWrapper { +.dataTable > .dataTableWrapper { width: 450px; - /* not more than 450px to make sure 2 tables can fit horizontally on a 1024 screen */ } -.dataTableVizAllColumns > .dataTableWrapper { - width: 535px; -} - -.dataTableActions > .dataTableWrapper, -.dataTableGraph > .dataTableWrapper { - width: 500px; - min-height: 1px; -} - -.dataTableEvolutionGraph > .dataTableWrapper { - width: 100%; -} - -.ui-dialog-content .dataTableWrapper { - width: 100%; +.subDataTable > .dataTableWrapper { + width: 95%; } .sortIconContainer { diff --git a/plugins/CoreHome/templates/_dataTable.twig b/plugins/CoreHome/templates/_dataTable.twig index 72ba84326c..c95fd78b56 100644 --- a/plugins/CoreHome/templates/_dataTable.twig +++ b/plugins/CoreHome/templates/_dataTable.twig @@ -1,8 +1,9 @@ {% set summaryRowId = constant('Piwik\\DataTable::ID_SUMMARY_ROW') %}{# ID_SUMMARY_ROW #} {% set isSubtable = javascriptVariablesToSet.idSubtable is defined and javascriptVariablesToSet.idSubtable != 0 %} -<div class="dataTable {{ properties.datatable_css_class }} {% if isSubtable %}subDataTable{% endif %}" +<div class="dataTable {{ visualizationCssClass }} {{ properties.datatable_css_class|default('') }} {% if isSubtable %}subDataTable{% endif %}" data-table-type="{{ properties.datatable_js_type|default('dataTable') }}" data-report="{{ properties.report_id }}" + data-props="{{ clientSidePropertiesToSet|json_encode }}" data-params="{{ javascriptVariablesToSet|json_encode }}"> <div class="reportDocumentation"> {% if properties.documentation|default is not empty %}<p>{{ properties.documentation|raw }}</p>{% endif %} diff --git a/plugins/CoreHome/templates/_dataTableActions_subDataTable.twig b/plugins/CoreHome/templates/_dataTableActions_subDataTable.twig index 72bf6607c1..696c4a5a72 100644 --- a/plugins/CoreHome/templates/_dataTableActions_subDataTable.twig +++ b/plugins/CoreHome/templates/_dataTableActions_subDataTable.twig @@ -1,14 +1,14 @@ {% if error is defined %} {{ error.message }} {% else %} - {% if (dataTable is empty or dataTable.getRowsCount() == 0) and not properties.show_expanded|default(false) %} + {% if (dataTable is empty or dataTable.getRowsCount() == 0) and not properties.visualization_properties.show_expanded|default(false) %} <tr> <td colspan="{{ properties.columns_to_display|length }}">{{ 'CoreHome_CategoryNoData'|translate }}</td> </tr> {% else %} {% for row in dataTable.getRows() %} {# display this row if it doesn't have a subtable or if we don't replace the row with the subtable #} - {% if not row.getIdSubDataTable() or not properties.show_expanded|default(false) or not properties.replace_row_with_subtable|default(false) %} + {% if not row.getIdSubDataTable() or not properties.visualization_properties.show_expanded|default(false) or not properties.replace_row_with_subtable|default(false) %} <tr {% if row.getIdSubDataTable() %}id="{{ row.getIdSubDataTable() }}"{% endif %} class="{{ row.getMetadata('css_class') }} {% if row.getIdSubDataTable() %}subDataTable{% endif %}"> {% for column in properties.columns_to_display %} @@ -20,8 +20,8 @@ {% endif %} {# display subtable if present and showing expanded datatable #} - {% if properties.show_expanded|default(false) and row.getIdSubDataTable() %} - {% include properties.subtable_template|default("@CoreHome/_dataTable.twig") with {'dataTable': row.getSubtable()} %} + {% if properties.visualization_properties.show_expanded|default(false) and row.getIdSubDataTable() %} + {% include properties.visualization_properties.subtable_template|default("@CoreHome/_dataTable.twig") with {'dataTable': row.getSubtable()} %} {% endif %} {% endfor %} {% endif %} diff --git a/plugins/CoreHome/templates/_dataTableFooter.twig b/plugins/CoreHome/templates/_dataTableFooter.twig index 676cf3ef9a..44b776b31d 100644 --- a/plugins/CoreHome/templates/_dataTableFooter.twig +++ b/plugins/CoreHome/templates/_dataTableFooter.twig @@ -77,6 +77,13 @@ <img width="16" height="16" src="plugins/Zeitgeist/images/tagcloud.png" title="{{ 'General_TagCloud'|translate }}"/> </a> {% endif %} + {% if properties.show_non_core_visualizations %} + {% for format, info in nonCoreVisualizations %} + <a class="tableIcon" format="{{ format }}" var="{{ format }}"> + <img width="16" height="16" src="{{ info.table_icon }}" title="{{ info.title|translate }}"/> + </a> + {% endfor %} + {% endif %} </span> </div> {% endif %} @@ -88,14 +95,14 @@ </span> <span class="exportToFormatItems" style="display:none"> {{ 'General_Export'|translate }}: - <a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="CSV" filter_limit="{{ properties.exportLimit }}">CSV</a> | - <a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="TSV" filter_limit="{{ properties.exportLimit }}">TSV (Excel)</a> | - <a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="XML" filter_limit="{{ properties.exportLimit }}">XML</a> | - <a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="JSON" filter_limit="{{ properties.exportLimit }}">Json</a> | - <a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="PHP" filter_limit="{{ properties.exportLimit }}">Php</a> + <a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="CSV" filter_limit="{{ properties.export_limit }}">CSV</a> | + <a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="TSV" filter_limit="{{ properties.export_limit }}">TSV (Excel)</a> | + <a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="XML" filter_limit="{{ properties.export_limit }}">XML</a> | + <a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="JSON" filter_limit="{{ properties.export_limit }}">Json</a> | + <a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="PHP" filter_limit="{{ properties.export_limit }}">Php</a> {% if properties.show_export_as_rss_feed %} | - <a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="RSS" filter_limit="{{ properties.exportLimit }}" date="last10"> + <a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="RSS" filter_limit="{{ properties.export_limit }}" date="last10"> <img border="0" src="plugins/Zeitgeist/images/feed.png"/> </a> {% endif %} @@ -130,7 +137,7 @@ {% endif %} </ul> </div> - {% if not properties.hide_annotations_view %} + {% if isPluginLoaded('Annotations') and not properties.hide_annotations_view %} <div class="annotationView" title="{{ 'Annotations_IconDesc_js'|translate }}"> <a class="tableIcon"> <img width="16" height="16" src="plugins/Zeitgeist/images/grey_marker.png"/> @@ -142,16 +149,16 @@ {% endif %} <div class="datatableRelatedReports"> - {% if (properties.relatedReports is not empty) and properties.show_related_reports %} - {% if properties.relatedReports|length == 1 %} + {% if (properties.related_reports is not empty) and properties.show_related_reports %} + {% if properties.related_reports|length == 1 %} {{ 'General_RelatedReport'|translate }}: {% else %} {{ 'General_RelatedReports'|translate }}: {% endif %} - <ul style="list-style:none;{% if properties.relatedReports|length == 1 %}display:inline-block;{% endif %}}"> + <ul style="list-style:none;{% if properties.related_reports|length == 1 %}display:inline-block;{% endif %}}"> <li><span href="{{ properties.self_url }}" style="display:none;">{{ properties.title }}</span></li> - {% for reportUrl,reportTitle in properties.relatedReports %} + {% for reportUrl,reportTitle in properties.related_reports %} <li><span href="{{ reportUrl }}">{{ reportTitle }}</span></li> {% endfor %} </ul> diff --git a/plugins/CoreHome/templates/_dataTableViz_jqplotGraph.twig b/plugins/CoreHome/templates/_dataTableViz_jqplotGraph.twig deleted file mode 100644 index 65e9f7a43c..0000000000 --- a/plugins/CoreHome/templates/_dataTableViz_jqplotGraph.twig +++ /dev/null @@ -1,10 +0,0 @@ -<div class="jqplot-graph"> - <div class="piwik-graph" - style="width: {{ properties.graph_width }}; height: {{ properties.graph_height }};" - data-data="{{ data|e('html') }}" - {% if properties.external_series_toggle is defined and properties.external_series_toggle %} - data-external-series-toggle="{{ properties.external_series_toggle }}" - data-external-series-show-all="{% if properties.external_series_toggle_show_all %}1{% else %}0{% endif %}" - {% endif %}> - </div> -</div>
\ No newline at end of file diff --git a/plugins/CoreHome/templates/getMultiRowEvolutionPopover.twig b/plugins/CoreHome/templates/getMultiRowEvolutionPopover.twig index 16ef990aa6..c0a043aac8 100644 --- a/plugins/CoreHome/templates/getMultiRowEvolutionPopover.twig +++ b/plugins/CoreHome/templates/getMultiRowEvolutionPopover.twig @@ -1,4 +1,4 @@ -{% set seriesColorCount = constant("Evolution::SERIES_COLOR_COUNT") %} +{% set seriesColorCount = constant("Piwik\\Plugins\\CoreVisualizations\\Visualizations\\JqplotGraph\\Evolution::SERIES_COLOR_COUNT") %} <div class="rowevolution multirowevolution"> <div class="popover-title">{{ 'RowEvolution_MultiRowEvolutionTitle'|translate }}</div> <div class="graph"> diff --git a/plugins/CoreHome/templates/getRowEvolutionPopover.twig b/plugins/CoreHome/templates/getRowEvolutionPopover.twig index fab9fb2437..74b6cbbfe4 100644 --- a/plugins/CoreHome/templates/getRowEvolutionPopover.twig +++ b/plugins/CoreHome/templates/getRowEvolutionPopover.twig @@ -1,4 +1,4 @@ -{% set seriesColorCount = constant("Evolution::SERIES_COLOR_COUNT") %} +{% set seriesColorCount = constant("Piwik\\Plugins\\CoreVisualizations\\Visualizations\\JqplotGraph\\Evolution::SERIES_COLOR_COUNT") %} <div class="rowevolution"> <div class="popover-title">{{ popoverTitle | raw }}</div> <div class="graph"> diff --git a/plugins/CoreVisualizations/CoreVisualizations.php b/plugins/CoreVisualizations/CoreVisualizations.php new file mode 100644 index 0000000000..00330377a2 --- /dev/null +++ b/plugins/CoreVisualizations/CoreVisualizations.php @@ -0,0 +1,58 @@ +<?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_Plugins + * @package Piwik_CoreVisualizations + */ + +use Piwik\Plugin; + +require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/JqplotDataGenerator.php'; +require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/Cloud.php'; +require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/HtmlTable.php'; +require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/JqplotGraph.php'; + +/** + * This plugin contains all core visualizations, such as the normal HTML table and + * jqPlot graphs. + */ +class Piwik_CoreVisualizations extends Plugin +{ + /** + * @see Piwik_Plugin::getListHooksRegistered + */ + public function getListHooksRegistered() + { + return array( + 'AssetManager.getCssFiles' => 'getCssFiles', + 'AssetManager.getJsFiles' => 'getJsFiles', + 'DataTableVisualization.getAvailable' => 'getAvailableDataTableVisualizations', + ); + } + + public function getAvailableDataTableVisualizations(&$visualizations) + { + $visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\HtmlTable'; + $visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\HtmlTable\\AllColumns'; + $visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\HtmlTable\\Goals'; + $visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\Cloud'; + $visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\JqplotGraph\\Pie'; + $visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\JqplotGraph\\Bar'; + $visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\JqplotGraph\\Evolution'; + } + + public function getCssFiles(&$cssFiles) + { + $cssFiles[] = "plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less"; + $cssFiles[] = "plugins/CoreVisualizations/stylesheets/jqplot.css"; + } + + public function getJsFiles(&$jsFiles) + { + $jsFiles[] = "plugins/CoreVisualizations/javascripts/jqplot.js"; + } +}
\ No newline at end of file diff --git a/core/JqplotDataGenerator.php b/plugins/CoreVisualizations/JqplotDataGenerator.php index 039370b251..6e9d707224 100644 --- a/core/JqplotDataGenerator.php +++ b/plugins/CoreVisualizations/JqplotDataGenerator.php @@ -9,7 +9,7 @@ * @package Piwik */ -namespace Piwik; +namespace Piwik\Plugins\CoreVisualizations; use Exception; use Piwik\Common; @@ -17,6 +17,8 @@ use Piwik\Metrics; use Piwik\DataTable; use Piwik\Visualization; +require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php'; + /** * Generates JSON data used to configure and populate JQPlot graphs. * @@ -82,8 +84,8 @@ class JqplotDataGenerator */ public function generate($dataTable) { - if (!empty($this->properties['graph_limit'])) { - $offsetStartSummary = $this->properties['graph_limit'] - 1; + if (!empty($this->properties['visualization_properties']->max_graph_elements)) { + $offsetStartSummary = $this->properties['visualization_properties']->max_graph_elements - 1; $sortColumn = !empty($this->properties['filter_sort_column']) ? $this->properties['filter_sort_column'] : Metrics::INDEX_NB_VISITS; @@ -95,7 +97,7 @@ class JqplotDataGenerator 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'])) { + if (!empty($this->properties['visualization_properties']->add_total_row)) { $dataTable->queueFilter('AddSummaryRow', array(0, Piwik_Translate('General_Total'), null, false)); } @@ -136,10 +138,11 @@ class JqplotDataGenerator $visualization->setAxisYValues($columnNameToValue); $visualization->setAxisYLabels($columnNameToTranslation); $visualization->setAxisYUnit($this->properties['y_axis_unit']); - $visualization->setDisplayPercentageInTooltip($this->properties['display_percentage_in_tooltip']); + $visualization->setDisplayPercentageInTooltip( + $this->properties['visualization_properties']->display_percentage_in_tooltip); // show_all_ticks is not real query param, it is set by GenerateGraphHTML. - if ($this->properties['show_all_ticks']) { + if ($this->properties['visualization_properties']->show_all_ticks) { $visualization->showAllTicks(); } @@ -186,11 +189,12 @@ class JqplotDataGenerator */ protected function addSeriesPickerToView() { - if (count($this->properties['selectable_columns']) - && Common::getRequestVar('showSeriesPicker', $this->properties['show_series_picker']) == 1 + $defaultShowSeriesPicker = $this->properties['visualization_properties']->show_series_picker; + if (count($this->properties['visualization_properties']->selectable_columns) + && Common::getRequestVar('showSeriesPicker', $defaultShowSeriesPicker) == 1 ) { $selectableColumns = array(); - foreach ($this->properties['selectable_columns'] as $column) { + foreach ($this->properties['visualization_properties']->selectable_columns as $column) { $selectableColumns[] = array( 'column' => $column, 'translation' => @$this->properties['translations'][$column], @@ -199,7 +203,7 @@ class JqplotDataGenerator } $this->visualization->setSelectableColumns( - $selectableColumns, $this->properties['allow_multi_select_series_picker']); + $selectableColumns, $this->properties['visualization_properties']->allow_multi_select_series_picker); } } } diff --git a/core/JqplotDataGenerator/Evolution.php b/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php index dab684cb0a..c24ddbb279 100644 --- a/core/JqplotDataGenerator/Evolution.php +++ b/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php @@ -8,14 +8,14 @@ * @category Piwik * @package Piwik */ -namespace Piwik\JqplotDataGenerator; +namespace Piwik\Plugins\CoreVisualizations\JqplotDataGenerator; use Piwik\Piwik; use Piwik\Common; use Piwik\DataTable; use Piwik\ViewDataTable; use Piwik\Url; -use Piwik\JqplotDataGenerator; +use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator; /** * Generates JQPlot JSON data/config for evolution graphs. @@ -70,7 +70,7 @@ class Evolution extends JqplotDataGenerator // 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) { + if ($this->properties['row_picker_match_rows_by'] !== false) { $rowVisible = $this->handleRowForRowPicker($rowLabel); if (!$rowVisible) { continue; @@ -146,7 +146,7 @@ class Evolution extends JqplotDataGenerator $this->addSeriesPickerToView(); // configure the row picker - if ($this->properties['row_picker_mach_rows_by'] !== false) { + if ($this->properties['row_picker_match_rows_by'] !== false) { $visualization->setSelectableRows(array_values($this->rowPickerConfig)); } } @@ -162,7 +162,7 @@ class Evolution extends JqplotDataGenerator { // determine whether row is visible $isVisible = true; - if ($this->properties['row_picker_mach_rows_by'] == 'label') { + if ($this->properties['row_picker_match_rows_by'] == 'label') { $isVisible = in_array($rowLabel, $this->properties['row_picker_visible_rows']); } diff --git a/core/Visualization/Cloud.php b/plugins/CoreVisualizations/Visualizations/Cloud.php index 1a12f4061c..a9a8eb7364 100644 --- a/core/Visualization/Cloud.php +++ b/plugins/CoreVisualizations/Visualizations/Cloud.php @@ -8,7 +8,7 @@ * @category Piwik * @package Piwik */ -namespace Piwik\Visualization; +namespace Piwik\Plugins\CoreVisualizations\Visualizations; use Piwik\Common; use Piwik\View; @@ -26,12 +26,23 @@ use Piwik\DataTableVisualization; */ class Cloud extends DataTableVisualization { + const ID = 'cloud'; + /** Used by integration tests to make sure output is consistent. */ public static $debugDisableShuffle = false; protected $wordsArray = array(); public $truncatingLimit = 50; + public static function getDefaultPropertyValues() + { + return array( + 'show_offset_information' => false, + 'show_exclude_low_population' => false, + 'display_logo_instead_of_label' => false, + ); + } + /** * Assign word to array * @param string $word @@ -56,7 +67,7 @@ class Cloud extends DataTableVisualization */ public function render($dataTable, $properties) { - $view = new View("@CoreHome/_dataTableViz_tagCloud.twig"); + $view = new View("@CoreVisualizations/_dataTableViz_tagCloud.twig"); $view->properties = $properties; $columnToDisplay = $properties['columns_to_display'][1]; diff --git a/core/Visualization/HtmlTable.php b/plugins/CoreVisualizations/Visualizations/HtmlTable.php index 04519ae922..8fd87fc638 100644 --- a/core/Visualization/HtmlTable.php +++ b/plugins/CoreVisualizations/Visualizations/HtmlTable.php @@ -8,8 +8,7 @@ * @category Piwik * @package Piwik */ - -namespace Piwik\Visualization; +namespace Piwik\Plugins\CoreVisualizations\Visualizations; use Piwik\Piwik; use Piwik\DataTable; @@ -21,6 +20,9 @@ use Piwik\DataTableVisualization; use Piwik\DataTable\Filter\AddColumnsProcessedMetricsGoal; use \Piwik_Goals_API; +require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php'; +require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/HtmlTable/Goals.php'; + /** * DataTable visualization that shows DataTable data in an HTML table. */ @@ -28,16 +30,64 @@ 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' + /** + * Custom template used if displaying a subtable. + * + * TODO: Should be replaced w/ allowing custom visualization for + * subtables. Should not directly touch template. + */ + const SUBTABLE_TEMPLATE = 'subtable_template'; + + /** + * Controls whether the entire DataTable should be rendered (including subtables) or just one + * specific table in the tree. + */ + const SHOW_EXPANDED = 'show_expanded'; + + /** + * Controls whether any DataTable Row Action icons are shown. If true, no icons are shown. + * + * @see also self::DISABLE_ROW_EVOLUTION + */ + const DISABLE_ROW_ACTIONS = 'disable_row_actions'; + + /** + * Controls whether the row evolution DataTable Row Action icon is shown or not. + * + * @see also self::DISABLE_ROW_ACTIONS + */ + const DISABLE_ROW_EVOLUTION = 'disable_row_evolution'; + + /** + * 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. + */ + const SHOW_EXTRA_COLUMNS = 'show_extra_columns'; + + /** + * If true, conversions for each existing goal will be displayed for the visits in + * each row. + */ + const SHOW_GOALS_COLUMNS = 'show_goals_columns'; + + /** + * If true, subtables will not be loaded when rows are clicked, but only if the + * 'show_goals_columns' property is also true. + * + * @see also self::SHOW_GOALS_COLUMNS + */ + const DISABLE_SUBTABLE_IN_GOALS_VIEW = 'disable_subtable_when_show_goals'; + + static public $clientSideParameters = array( + 'search_recursive', ); - static public $overridableProperties = array( + static public $clientSideProperties = array( 'show_extra_columns', - 'show_goals_columns' + 'show_goals_columns', + 'disable_row_evolution', + 'disable_row_actions' ); /** @@ -45,15 +95,19 @@ class HtmlTable extends DataTableVisualization */ public function __construct($view) { - if ($view->show_extra_columns) { + if (Common::getRequestVar('idSubtable', false) + && $view->visualization_properties->subtable_template + ) { + $view->datatable_template = $view->visualization_properties->subtable_template; + } + + if ($view->visualization_properties->show_extra_columns) { $this->setShowExtraColumnsProperties($view); } - if ($view->show_goals_columns) { + if ($view->visualization_properties->show_goals_columns) { $this->setShowGoalsColumnsProperties($view); } - - $view->defaultPropertiesTo($this->getDefaultPropertyValues()); } /** @@ -65,34 +119,26 @@ class HtmlTable extends DataTableVisualization */ public function render($dataTable, $properties) // TODO: $properties should be a viewdatatable, I think. { - $view = new View("@CoreHome/_dataTableViz_htmlTable.twig"); + $view = new View("@CoreVisualizations/_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() + public static 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 + 'visualization_properties' => array( + 'disable_row_evolution' => false, + 'disable_row_actions' => false, + 'subtable_template' => "@CoreHome/_dataTable.twig", + 'show_extra_columns' => false, + 'show_goals_columns' => false, + 'disable_subtable_when_show_goals' => false, + ), ); if (Common::getRequestVar('enable_filter_excludelowpop', false) == '1') { @@ -153,7 +199,7 @@ class HtmlTable extends DataTableVisualization array('<br />', '<br />', '<a href="http://piwik.org/docs/tracking-goals-web-analytics/" target="_blank">', '</a>')); } - if (!$view->disable_subtable_when_show_goals) { + if (!$view->visualization_properties->disable_subtable_when_show_goals) { $view->subtable_controller_action = null; } diff --git a/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php b/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php new file mode 100644 index 0000000000..9078c999d9 --- /dev/null +++ b/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php @@ -0,0 +1,34 @@ +<?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\Plugins\CoreVisualizations\Visualizations\HtmlTable; + +use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; + +/** + * TODO + */ +class AllColumns extends HtmlTable +{ + const ID = 'tableAllColumns'; + + /** + * Constructor. + * + * @param ViewDataTable $view + */ + public function __construct($view) + { + $view->visualization_properties->show_extra_columns = true; + + parent::__construct($view); + } +}
\ No newline at end of file diff --git a/plugins/CoreVisualizations/Visualizations/HtmlTable/Goals.php b/plugins/CoreVisualizations/Visualizations/HtmlTable/Goals.php new file mode 100644 index 0000000000..f1da411fb4 --- /dev/null +++ b/plugins/CoreVisualizations/Visualizations/HtmlTable/Goals.php @@ -0,0 +1,34 @@ +<?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\Plugins\CoreVisualizations\Visualizations\HtmlTable; + +use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; + +/** + * TODO + */ +class Goals extends HtmlTable +{ + const ID = 'tableGoals'; + + /** + * Constructor. + * + * @param ViewDataTable $view + */ + public function __construct($view) + { + $view->visualization_properties->show_goals_columns = true; + + parent::__construct($view); + } +}
\ No newline at end of file diff --git a/plugins/CoreVisualizations/Visualizations/JqplotGraph.php b/plugins/CoreVisualizations/Visualizations/JqplotGraph.php new file mode 100644 index 0000000000..8ed5f24840 --- /dev/null +++ b/plugins/CoreVisualizations/Visualizations/JqplotGraph.php @@ -0,0 +1,206 @@ +<?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\Plugins\CoreVisualizations\Visualizations; + +use Piwik\Common; +use Piwik\View; +use Piwik\DataTable; +use Piwik\DataTableVisualization; +use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator; + +require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php'; +require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php'; +require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/JqplotGraph/Evolution.php'; + +/** + * 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; + + /** + * Whether the series picker should allow picking more than one series or not. + */ + const ALLOW_MULTI_SELECT_SERIES_PICKER = 'allow_multi_select_series_picker'; + + /** + * The maximum number of elements to render when rendering a jqPlot graph. All other elements + * will be aggregated in an 'Others' element. + */ + const MAX_GRAPH_ELEMENTS = 'max_graph_elements'; + + /** + * The name of the JavaScript class to use as this graph's external series toggle. The class + * must be a subclass of JQPlotExternalSeriesToggle. + * + * @see self::EXTERNAL_SERIES_TOGGLE_SHOW_ALL + */ + const EXTERNAL_SERIES_TOGGLE = 'external_series_toggle'; + + /** + * Whether the graph should show all loaded series upon initial display. + * + * @see self::EXTERNAL_SERIES_TOGGLE + */ + const EXTERNAL_SERIES_TOGGLE_SHOW_ALL = 'external_series_toggle_show_all'; + + /** + * Array property that contains the names of columns that can be selected in the Series Picker. + */ + const SELECTABLE_COLUMNS = 'selectable_columns'; + + /** + * Controls whether all ticks & labels are shown on a graph's x-axis or just some. + */ + const SHOW_ALL_TICKS = 'show_all_ticks'; + + /** + * If true, a row with totals of each DataTable column is added. + */ + const ADD_TOTAL_ROW = 'add_total_row'; + + /** + * The CSS width of the graph. (eg, '100px'). + */ + const GRAPH_WIDTH = 'graph_width'; + + /** + * The CSS height of the graph. (eg, '100px'). + */ + const GRAPH_HEIGHT = 'graph_height'; + + /** + * Controls whether the Series Picker is shown or not. The Series Picker allows users to + * choose between displaying data of different columns. + */ + const SHOW_SERIES_PICKER = 'show_series_picker'; + + /** + * 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. + */ + const DISPLAY_PERCENTAGE_IN_TOOLTIP = 'display_percentage_in_tooltip'; + + /** + * Constructor. + * + * @param Piwik\ViewDataTable $view + */ + 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; + + if ($view->show_goals) { + $goalMetrics = array('nb_conversions', 'revenue'); + $view->visualization_properties->selectable_columns = array_merge( + $view->visualization_properties->selectable_columns, $goalMetrics); + + $view->translations['nb_conversions'] = Piwik_Translate('Goals_ColumnConversions'); + $view->translations['revenue'] = Piwik_Translate('General_TotalRevenue'); + } + + // 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'; + } + } + + /** + * Returns an array mapping property names with default values for this visualization. + * + * @return array + */ + public static function getDefaultPropertyValues() + { + // selectable columns + $selectableColumns = array('nb_visits', 'nb_actions'); + if (Common::getRequestVar('period', false) == 'day') { // TODO: should depend on columns datatable has. + $selectableColumns[] = 'nb_uniq_visitors'; + } + + return array( + 'show_offset_information' => false, + 'show_pagination_control' => false, + 'show_exclude_low_population' => false, + 'show_search' => false, + 'show_export_as_image_icon' => true, + 'y_axis_unit' => '', + 'row_picker_match_rows_by' => false, + 'row_picker_visible_rows' => array(), + 'visualization_properties' => array( + 'JqplotGraph' => array( + 'add_total_row' => 0, + 'show_all_ticks' => false, + 'allow_multi_select_series_picker' => true, + 'max_graph_elements' => false, + 'selectable_columns' => $selectableColumns, + 'graph_width' => '100%', + 'graph_height' => self::DEFAULT_GRAPH_HEIGHT . 'px', + 'external_series_toggle' => false, + 'external_series_toggle_show_all' => false, + 'show_series_picker' => true, + 'display_percentage_in_tooltip' => true, + ) + ) + ); + } + + /** + * Renders this visualization. + * + * @param DataTable $dataTable + * @param array $properties View Properties. + * @return string + */ + public function render($dataTable, $properties) + { + $view = new View("@CoreVisualizations/_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/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php new file mode 100644 index 0000000000..dffe7393f2 --- /dev/null +++ b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php @@ -0,0 +1,35 @@ +<?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\Plugins\CoreVisualizations\Visualizations\JqplotGraph; + +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph; +use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator; + +/** + * Visualization that renders HTML for a Bar graph using jqPlot. + */ +class Bar extends JqplotGraph +{ + const ID = 'graphVerticalBar'; + + public static function getDefaultPropertyValues() + { + $result = parent::getDefaultPropertyValues(); + $result['visualization_properties']['max_graph_elements'] = 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/plugins/CoreVisualizations/Visualizations/JqplotGraph/Evolution.php index 5d7734061b..684b3fb524 100644 --- a/core/Visualization/JqplotGraph/Evolution.php +++ b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Evolution.php @@ -9,29 +9,30 @@ * @package Piwik */ -namespace Piwik\Visualization\JqplotGraph; +namespace Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph; use Piwik\Common; use Piwik\Site; use Piwik\Controller; use Piwik\Period\Range; -use Piwik\Visualization\JqplotGraph; -use Piwik\JqplotDataGenerator; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph; +use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator; /** - * TODO + * Visualization that renders HTML for a line graph using jqPlot. */ class Evolution extends JqplotGraph { - const GRAPH_HEIGHT = 170; const ID = 'graphEvolution'; + + const GRAPH_HEIGHT = 170; + const SERIES_COLOR_COUNT = 8; 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... + // period will be overridden when 'range' is requested in the UI // 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') { @@ -41,15 +42,15 @@ class Evolution extends JqplotGraph $this->calculateEvolutionDateRange($view); } - protected function getDefaultPropertyValues($view) + public static function getDefaultPropertyValues() { - $result = parent::getDefaultPropertyValues($view); - $result['graph_height'] = self::GRAPH_HEIGHT . 'px'; + $result = parent::getDefaultPropertyValues(); $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; + $result['visualization_properties']['Evolution']['graph_height'] = self::GRAPH_HEIGHT . 'px'; return $result; } diff --git a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php new file mode 100644 index 0000000000..8ca39659b1 --- /dev/null +++ b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php @@ -0,0 +1,36 @@ +<?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\Plugins\CoreVisualizations\Visualizations\JqplotGraph; + +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph; +use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator; + +/** + * Visualization that renders HTML for a Pie graph using jqPlot. + */ +class Pie extends JqplotGraph +{ + const ID = 'graphPie'; + + public static function getDefaultPropertyValues() + { + $result = parent::getDefaultPropertyValues(); + $result['visualization_properties']['max_graph_elements'] = 6; + $result['visualization_properties']['allow_multi_select_series_picker'] = false; + return $result; + } + + protected function makeDataGenerator($properties) + { + return JqplotDataGenerator::factory('pie', $properties); + } +}
\ No newline at end of file diff --git a/plugins/CoreHome/javascripts/jqplot.js b/plugins/CoreVisualizations/javascripts/jqplot.js index dde65a3977..f8888dbd0f 100644 --- a/plugins/CoreHome/javascripts/jqplot.js +++ b/plugins/CoreVisualizations/javascripts/jqplot.js @@ -68,21 +68,17 @@ JQPlot.prototype = { /** Generic render function */ render: function (targetDivId, lang) { - var type = $('#' + targetDivId).closest('div.dataTable').data('dataTableInstance').param['viewDataTable']; + var dataTableDiv = $('#' + targetDivId).closest('div.dataTable'); // preapare the appropriate chart type - switch (type) {// TODO: should rely on CSS, not viewDataTable. otherwise ecommerceOrder row evolution will fail - case 'graphEvolution': - this.prepareEvolutionChart(targetDivId, lang); - break; - case 'graphVerticalBar': - this.prepareBarChart(targetDivId, lang); - break; - case 'graphPie': - this.preparePieChart(targetDivId, lang); - break; - default: - return; + if (dataTableDiv.hasClass('dataTableVizEvolution')) { + this.prepareEvolutionChart(targetDivId, lang); + } else if (dataTableDiv.hasClass('dataTableVizBar')) { + this.prepareBarChart(targetDivId, lang); + } else if (dataTableDiv.hasClass('dataTableVizPie')) { + this.preparePieChart(targetDivId, lang); + } else { + return; } // handle replot @@ -620,7 +616,8 @@ JQPlot.prototype = { var namespace = graphType + '-graph-colors'; - this.params.seriesColors = colorManager.getColors(namespace, seriesColorNames, true); + this.originalData.params.seriesColors = this.params.seriesColors = + colorManager.getColors(namespace, seriesColorNames, true); this.params.grid.background = colorManager.getColor(namespace, 'grid-background'); this.params.grid.borderColor = colorManager.getColor(namespace, 'grid-border'); this.tickColor = colorManager.getColor(namespace, 'ticks'); diff --git a/plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less b/plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less new file mode 100644 index 0000000000..5ae202f36f --- /dev/null +++ b/plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less @@ -0,0 +1,18 @@ +/* container of each table */ +.dataTableVizHtmlTable > .dataTableWrapper { + width: 450px; + /* not more than 450px to make sure 2 tables can fit horizontally on a 1024 screen */ +} + +.dataTableVizAllColumns > .dataTableWrapper { + width: 535px; +} + +.dataTableVizPie > .dataTableWrapper, .dataTableVizBar > .dataTableWrapper { + width: 500px; + min-height: 1px; +} + +.dataTableVizEvolution > .dataTableWrapper { + width: 100%; +}
\ No newline at end of file diff --git a/plugins/CoreHome/stylesheets/jqplot.css b/plugins/CoreVisualizations/stylesheets/jqplot.css index bd5dec8e59..bd5dec8e59 100644 --- a/plugins/CoreHome/stylesheets/jqplot.css +++ b/plugins/CoreVisualizations/stylesheets/jqplot.css diff --git a/plugins/CoreHome/templates/_dataTableViz_htmlTable.twig b/plugins/CoreVisualizations/templates/_dataTableViz_htmlTable.twig index aa3911f628..1347218af0 100644 --- a/plugins/CoreHome/templates/_dataTableViz_htmlTable.twig +++ b/plugins/CoreVisualizations/templates/_dataTableViz_htmlTable.twig @@ -7,7 +7,7 @@ {%- set shouldHighlightRow = rowId == constant('Piwik\\DataTable::ID_SUMMARY_ROW') and properties.highlight_summary_row -%} {# display this row if it doesn't have a subtable or if we don't replace the row with the subtable #} - {% if not rowHasSubtable or not properties.show_expanded|default(false) or not properties.replace_row_with_subtable|default(false) %} + {% if not rowHasSubtable or not properties.visualization_properties.show_expanded|default(false) or not properties.replace_row_with_subtable|default(false) %} <tr {% if rowHasSubtable %}id="{{ row.getIdSubDataTable() }}"{% endif %} class="{{ row.getMetadata('css_class') }} {% if rowHasSubtable %}subDataTable{% endif %}{% if shouldHighlightRow %} highlight{% endif %}"> {% for column in properties.columns_to_display %} @@ -19,8 +19,8 @@ {% endif %} {# display subtable if present and showing expanded datatable #} - {% if properties.show_expanded|default(false) and rowHasSubtable %} - {% include properties.subtable_template with {'dataTable': row.getSubtable()} %} + {% if properties.visualization_properties.show_expanded|default(false) and rowHasSubtable %} + {% include properties.visualization_properties.subtable_template with {'dataTable': row.getSubtable()} %} {% endif %} {% endfor %} </tbody> diff --git a/plugins/CoreVisualizations/templates/_dataTableViz_jqplotGraph.twig b/plugins/CoreVisualizations/templates/_dataTableViz_jqplotGraph.twig new file mode 100644 index 0000000000..e980275ac6 --- /dev/null +++ b/plugins/CoreVisualizations/templates/_dataTableViz_jqplotGraph.twig @@ -0,0 +1,10 @@ +<div class="jqplot-graph"> + <div class="piwik-graph" + style="width: {{ properties.visualization_properties.graph_width }}; height: {{ properties.visualization_properties.graph_height }};" + data-data="{{ data|e('html') }}" + {% if properties.visualization_properties.external_series_toggle %} + data-external-series-toggle="{{ properties.visualization_properties.external_series_toggle }}" + data-external-series-show-all="{% if properties.visualization_properties.external_series_toggle_show_all %}1{% else %}0{% endif %}" + {% endif %}> + </div> +</div>
\ No newline at end of file diff --git a/plugins/CoreHome/templates/_dataTableViz_tagCloud.twig b/plugins/CoreVisualizations/templates/_dataTableViz_tagCloud.twig index 1513daca34..1513daca34 100644 --- a/plugins/CoreHome/templates/_dataTableViz_tagCloud.twig +++ b/plugins/CoreVisualizations/templates/_dataTableViz_tagCloud.twig diff --git a/plugins/DBStats/DBStats.php b/plugins/DBStats/DBStats.php index def12d84a4..593f2a9b64 100644 --- a/plugins/DBStats/DBStats.php +++ b/plugins/DBStats/DBStats.php @@ -105,7 +105,7 @@ class Piwik_DBStats extends Plugin $result['show_offset_information'] = false; $result['show_pagination_control'] = false; - $result['show_all_ticks'] = true; + $result['visualization_properties']['JqplotGraph']['show_all_ticks'] = true; // translate the labels themselves $valueToTranslationStr = array( @@ -146,7 +146,7 @@ class Piwik_DBStats extends Plugin $this->addPresentationFilters($result); $result['title'] = Piwik_Translate('DBStats_MetricTables'); - $result['relatedReports'] = array( + $result['related_reports'] = array( 'DBStats.getMetricDataSummaryByYear' => Piwik_Translate('DBStats_MetricDataByYear') ); @@ -161,7 +161,7 @@ class Piwik_DBStats extends Plugin $result['translations']['label'] = Piwik_Translate('CoreHome_PeriodYear'); $result['title'] = Piwik_Translate('DBStats_MetricDataByYear'); - $result['relatedReports'] = array( + $result['related_reports'] = array( 'DBStats.getMetricDataSummary' => Piwik_Translate('DBStats_MetricTables') ); @@ -175,7 +175,7 @@ class Piwik_DBStats extends Plugin $this->addPresentationFilters($result); $result['title'] = Piwik_Translate('DBStats_ReportTables'); - $result['relatedReports'] = array( + $result['related_reports'] = array( 'DBStats.getReportDataSummaryByYear' => Piwik_Translate('DBStats_ReportDataByYear') ); @@ -190,7 +190,7 @@ class Piwik_DBStats extends Plugin $result['translations']['label'] = Piwik_Translate('CoreHome_PeriodYear'); $result['title'] = Piwik_Translate('DBStats_ReportDataByYear'); - $result['relatedReports'] = array( + $result['related_reports'] = array( 'DBStats.getReportDataSummary' => Piwik_Translate('DBStats_ReportTables') ); @@ -256,7 +256,7 @@ class Piwik_DBStats extends Plugin $properties['show_tag_cloud'] = false; $properties['show_table_all_columns'] = false; $properties['keep_summary_row'] = true; - $properties['disable_row_evolution'] = true; + $properties['visualization_properties']['HtmlTable']['disable_row_evolution'] = true; $properties['translations'] = array( 'label' => Piwik_Translate('DBStats_Table'), 'year' => Piwik_Translate('CoreHome_PeriodYear'), diff --git a/plugins/Dashboard/stylesheets/dashboard.less b/plugins/Dashboard/stylesheets/dashboard.less index 376a160f72..f00fe46366 100644 --- a/plugins/Dashboard/stylesheets/dashboard.less +++ b/plugins/Dashboard/stylesheets/dashboard.less @@ -135,6 +135,11 @@ text-shadow: 1px 1px 2px #7e7363; } +// Overriding some dataTable css for better dashboard display +.widget .dataTableWrapper { + width: 100% !important; +} + .button { cursor: pointer; } diff --git a/plugins/DevicesDetection/DevicesDetection.php b/plugins/DevicesDetection/DevicesDetection.php index 8adc51141c..35fb148866 100644 --- a/plugins/DevicesDetection/DevicesDetection.php +++ b/plugins/DevicesDetection/DevicesDetection.php @@ -318,7 +318,7 @@ class Piwik_DevicesDetection extends Plugin 'show_exclude_low_population' => false, 'translations' => array('label' => Piwik_Translate("DevicesDetection_dataTableLabelSystemFamily")), 'title' => Piwik_Translate('DeviceDetection_OperatingSystemFamilies'), - 'relatedReports' => $this->getOsRelatedReports() + 'related_reports' => $this->getOsRelatedReports() ); } @@ -329,7 +329,7 @@ class Piwik_DevicesDetection extends Plugin 'show_exclude_low_population' => false, 'translations' => array('label' => Piwik_Translate("DevicesDetection_dataTableLabelSystemVersion")), 'title' => Piwik_Translate('DeviceDetection_OperatingSystemVersions'), - 'relatedReports' => $this->getOsRelatedReports() + 'related_reports' => $this->getOsRelatedReports() ); } @@ -340,7 +340,7 @@ class Piwik_DevicesDetection extends Plugin 'show_exclude_low_population' => false, 'translations' => array('label' => Piwik_Translate("DevicesDetection_dataTableLabelBrowserFamily")), 'title' => Piwik_Translate('DevicesDetection_BrowsersFamily'), - 'relatedReports' => $this->getBrowserRelatedReports() + 'related_reports' => $this->getBrowserRelatedReports() ); } @@ -350,7 +350,7 @@ class Piwik_DevicesDetection extends Plugin 'show_search' => false, 'show_exclude_low_population' => false, 'translations' => array('label' => Piwik_Translate("DevicesDetection_dataTableLabelBrowserVersion")), - 'relatedReports' => $this->getBrowserRelatedReports() + 'related_reports' => $this->getBrowserRelatedReports() ); } @@ -369,4 +369,4 @@ class Piwik_DevicesDetection extends Plugin 'DevicesDetection.getBrowserVersions' => Piwik_Translate('DevicesDetection_BrowserVersions') ); } -} +}
\ No newline at end of file diff --git a/plugins/ExampleUI/Controller.php b/plugins/ExampleUI/Controller.php index 5660190aaa..1c35d6b498 100644 --- a/plugins/ExampleUI/Controller.php +++ b/plugins/ExampleUI/Controller.php @@ -25,11 +25,11 @@ class Piwik_ExampleUI_Controller extends Controller $view->translations['label'] = "Hour of day"; $view->filter_sort_column = 'label'; $view->filter_sort_order = 'asc'; - $view->graph_limit = 24; + $view->visualization_properties->max_graph_elements = 24; $view->filter_limit = 24; $view->show_exclude_low_population = false; $view->show_table_all_columns = false; - $view->disable_row_evolution = true; + $view->visualization_properties->disable_row_evolution = true; $view->y_axis_unit = '°C'; // useful if the user requests the bar graph echo $view->render(); } @@ -56,7 +56,7 @@ class Piwik_ExampleUI_Controller extends Controller 'graphVerticalBar', 'ExampleUI.getTemperatures', $controllerAction = 'ExampleUI.barGraph'); $view->translations['value'] = "Temperature"; $view->y_axis_unit = '°C'; - $view->graph_limit = 24; + $view->visualization_properties->max_graph_elements = 24; $view->show_footer = false; echo $view->render(); } @@ -67,7 +67,7 @@ class Piwik_ExampleUI_Controller extends Controller 'graphPie', 'ExampleUI.getPlanetRatios', $controllerAction = 'ExampleUI.pieGraph'); $view->columns_to_display = array('value'); $view->translations['value'] = "times the diameter of Earth"; - $view->graph_limit = 10; + $view->visualization_properties->max_graph_elements = 10; $view->show_footer_icons = false; echo $view->render(); } diff --git a/plugins/ExtraVisualizations/ExtraVisualizations.php b/plugins/ExtraVisualizations/ExtraVisualizations.php new file mode 100644 index 0000000000..540183802e --- /dev/null +++ b/plugins/ExtraVisualizations/ExtraVisualizations.php @@ -0,0 +1,47 @@ +<?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_Plugins + * @package Piwik_ExtraVisualizations + */ + +use Piwik\Plugin; + +require_once PIWIK_INCLUDE_PATH . '/plugins/ExtraVisualizations/Visualizations/Treemap.php'; + +/** + * This plugin contains spme extra DataTable visualizations, such as Treemap. + */ +class Piwik_ExtraVisualizations extends Plugin +{ + /** + * @see Piwik_Plugin::getListHooksRegistered + */ + public function getListHooksRegistered() + { + return array( + 'AssetManager.getCssFiles' => 'getCssFiles', + 'AssetManager.getJsFiles' => 'getJsFiles', + 'DataTableVisualization.getAvailable' => 'getAvailableDataTableVisualizations', + ); + } + + public function getAvailableDataTableVisualizations(&$visualizations) + { + $visualizations[] = "Piwik\\Plugins\\ExtraVisualizations\\Visualizations\\Treemap"; + } + + public function getCssFiles(&$cssFiles) + { + $cssFiles[] = "plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less"; + } + + public function getJsFiles(&$jsFiles) + { + //$jsFiles[] = ""; + } +}
\ No newline at end of file diff --git a/plugins/ExtraVisualizations/Visualizations/Treemap.php b/plugins/ExtraVisualizations/Visualizations/Treemap.php new file mode 100644 index 0000000000..f469be9a61 --- /dev/null +++ b/plugins/ExtraVisualizations/Visualizations/Treemap.php @@ -0,0 +1,31 @@ +<?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\Plugins\ExtraVisualizations\Visualizations; + +use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; + +/** + * TODO + */ +class Treemap extends HtmlTable +{ + const ID = 'treemap'; + const FOOTER_ICON = 'plugins/Zeitgeist/images/table.png'; + const FOOTER_ICON_TITLE = 'General_DisplaySimpleTable'; + + /** + * TODO + */ + public function __construct($view) + { + // TODO + } +}
\ No newline at end of file diff --git a/plugins/ExtraVisualizations/stylesheets/dataTableVisualizations.less b/plugins/ExtraVisualizations/stylesheets/dataTableVisualizations.less new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/plugins/ExtraVisualizations/stylesheets/dataTableVisualizations.less diff --git a/plugins/Goals/Controller.php b/plugins/Goals/Controller.php index 17a5e38fae..0bca7f1403 100644 --- a/plugins/Goals/Controller.php +++ b/plugins/Goals/Controller.php @@ -286,7 +286,7 @@ class Piwik_Goals_Controller extends Controller $view->translations[$columnName] = $columnTranslation; } $view->columns_to_display = $columns; - $view->selectable_columns = $selectableColumns; + $view->visualization_properties->selectable_columns = $selectableColumns; $langString = $idGoal ? 'Goals_SingleGoalOverviewDocumentation' : 'Goals_GoalsOverviewDocumentation'; $view->documentation = Piwik_Translate($langString, '<br />'); diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php index 4dc4685c59..010f1a6623 100644 --- a/plugins/Goals/Goals.php +++ b/plugins/Goals/Goals.php @@ -96,7 +96,7 @@ class Piwik_Goals extends Plugin 'Menu.add' => 'addMenus', 'SitesManager.deleteSite' => 'deleteSiteGoals', 'Goals.getReportsWithGoalMetrics' => 'getActualReportsWithGoalMetrics', - 'ViewDataTable.getReportDisplayProperties' => 'getReportDisplayProperties', // TODO: ViewDataTable should get ALL once + 'ViewDataTable.getReportDisplayProperties' => 'getReportDisplayProperties', ); return $hooks; } diff --git a/plugins/Live/Live.php b/plugins/Live/Live.php index dfa08ce8c5..dc199fe9d5 100644 --- a/plugins/Live/Live.php +++ b/plugins/Live/Live.php @@ -74,7 +74,6 @@ class Piwik_Live extends Plugin 'show_all_views_icons' => false, 'show_table_all_columns' => false, 'show_export_as_rss_feed' => false, - 'disable_row_actions' => true, 'documentation' => Piwik_Translate('Live_VisitorLogDocumentation', array('<br />', '<br />')), 'custom_parameters' => array( // set a very high row count so that the next link in the footer of the data table is always shown @@ -83,6 +82,11 @@ class Piwik_Live extends Plugin 'filterEcommerce' => Common::getRequestVar('filterEcommerce', 0, 'int'), 'pageUrlNotDefined' => Piwik_Translate('General_NotDefined', Piwik_Translate('Actions_ColumnPageURL')) ), + 'visualization_properties' => array( + 'HtmlTable' => array( + 'disable_row_actions' => true, + ) + ) ); } } diff --git a/plugins/PleineLune/stylesheets/_menuDashboard.less b/plugins/PleineLune/stylesheets/_menuDashboard.less index 8b5ded920e..060995b2c9 100644 --- a/plugins/PleineLune/stylesheets/_menuDashboard.less +++ b/plugins/PleineLune/stylesheets/_menuDashboard.less @@ -44,6 +44,7 @@ .nav_sep { border-color: @theme-color-box-border; border-width: 1px 0; + border-radius: 0; background: @theme-color-box-active; padding: 0 10px; margin-left: -10px; diff --git a/plugins/Referers/Controller.php b/plugins/Referers/Controller.php index c9362f4a01..1e31b6dd41 100644 --- a/plugins/Referers/Controller.php +++ b/plugins/Referers/Controller.php @@ -263,7 +263,7 @@ class Piwik_Referers_Controller extends Controller { $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Referers.getRefererType'); - $view->add_total_row = true; + $view->visualization_properties->add_total_row = true; // configure displayed columns if (empty($columns)) { @@ -279,7 +279,7 @@ class Piwik_Referers_Controller extends Controller } else { $selectable = array('nb_visits', 'nb_actions'); } - $view->selectable_columns = $selectable; + $view->visualization_properties->selectable_columns = $selectable; // configure displayed rows $visibleRows = Common::getRequestVar('rows', false); @@ -299,7 +299,7 @@ class Piwik_Referers_Controller extends Controller $visibleRows = array($label, $total); $view->request_parameters_to_modify['rows'] = $label . ',' . $total; } - $view->row_picker_mach_rows_by = 'label'; + $view->row_picker_match_rows_by = 'label'; $view->row_picker_visible_rows = $visibleRows; $view->documentation = Piwik_Translate('Referers_EvolutionDocumentation') . '<br />' diff --git a/plugins/Referers/Referers.php b/plugins/Referers/Referers.php index 4f72e81d93..36c4b85d08 100644 --- a/plugins/Referers/Referers.php +++ b/plugins/Referers/Referers.php @@ -332,10 +332,14 @@ class Piwik_Referers extends Plugin 'show_offset_information' => false, 'show_pagination_control' => false, 'show_exclude_low_population' => false, - 'disable_subtable_when_show_goals' => true, 'show_goals' => true, 'filter_limit' => 10, - 'translations' => array('label' => $labelColumnTitle) + 'translations' => array('label' => $labelColumnTitle), + 'visualization_properties' => array( + 'HtmlTable' => array( + 'disable_subtable_when_show_goals' => true, + ) + ), ); } @@ -347,7 +351,11 @@ class Piwik_Referers extends Plugin 'translations' => array('label' => Piwik_Translate('Referers_Referrer')), 'show_goals' => true, 'filter_limit' => 20, - 'custom_parameters' => array('disable_row_actions' => '1'), + 'visualization_properties' => array( + 'HtmlTable' => array( + 'disable_row_actions' => true + ) + ), 'filters' => array( array('MetadataCallbackAddMetadata', array('referrer_type', 'html_label_prefix', $setGetAllHtmlPrefix)) ) @@ -362,7 +370,11 @@ class Piwik_Referers extends Plugin 'translations' => array('label' => Piwik_Translate('Referers_ColumnKeyword')), 'show_goals' => true, 'filter_limit' => 25, - 'disable_subtable_when_show_goals' => true, + 'visualization_properties' => array( + 'HtmlTable' => array( + 'disable_subtable_when_show_goals' => true, + ) + ), ); } @@ -383,8 +395,12 @@ class Piwik_Referers extends Plugin 'show_exclude_low_population' => false, 'show_goals' => true, 'filter_limit' => 25, - 'disable_subtable_when_show_goals' => true, - 'translations' => array('label' => Piwik_Translate('Referers_ColumnSearchEngine')) + 'translations' => array('label' => Piwik_Translate('Referers_ColumnSearchEngine')), + 'visualization_properties' => array( + 'HtmlTable' => array( + 'disable_subtable_when_show_goals' => true, + ) + ), ); } @@ -404,8 +420,12 @@ class Piwik_Referers extends Plugin 'show_exclude_low_population' => false, 'show_goals' => true, 'filter_limit' => 25, - 'disable_subtable_when_show_goals' => true, - 'translations' => array('label' => Piwik_Translate('Referers_ColumnWebsite')) + 'translations' => array('label' => Piwik_Translate('Referers_ColumnWebsite')), + 'visualization_properties' => array( + 'HtmlTable' => array( + 'disable_subtable_when_show_goals' => true, + ) + ), ); } @@ -417,8 +437,12 @@ class Piwik_Referers extends Plugin 'show_exclude_low_population' => false, 'filter_limit' => 10, 'show_goals' => true, - 'disable_subtable_when_show_goals' => true, - 'translations' => array('label' => Piwik_Translate('Referers_ColumnSocial')) + 'translations' => array('label' => Piwik_Translate('Referers_ColumnSocial')), + 'visualization_properties' => array( + 'HtmlTable' => array( + 'disable_subtable_when_show_goals' => true, + ) + ), ); $widget = Common::getRequestVar('widget', false); diff --git a/plugins/UserSettings/UserSettings.php b/plugins/UserSettings/UserSettings.php index 3795d0d6fd..5d82f05a25 100644 --- a/plugins/UserSettings/UserSettings.php +++ b/plugins/UserSettings/UserSettings.php @@ -172,86 +172,110 @@ class Piwik_UserSettings extends Plugin ); return $hooks; } - + public function getReportDisplayProperties(&$properties) { - $basicUserSettingsProperties = array('show_search' => false, - 'show_exclude_low_population' => false, - 'filter_limit' => 5, - 'graph_limit' => 5); - - $osRelatedReports = array( - 'UserSettings.getOSFamily' => Piwik_Translate('UserSettings_OperatingSystemFamily'), - 'UserSettings.getOS' => Piwik_Translate('UserSettings_OperatingSystems') - ); - - $browserRelatedReports = array( - 'UserSettings.getBrowser' => Piwik_Translate('UserSettings_Browsers'), - 'UserSettings.getBrowserVersion' => Piwik_Translate('UserSettings_ColumnBrowserVersion') - ); - - $wideScreenDeviceTypeRelatedReports = array( - 'UserSettings.getMobileVsDesktop' => Piwik_Translate('UserSettings_MobileVsDesktop'), - 'UserSettings.getWideScreen' => Piwik_Translate('UserSettings_ColumnTypeOfScreen') - ); - - $properties['UserSettings.getResolution'] = array_merge($basicUserSettingsProperties, array( + $properties['UserSettings.getResolution'] = $this->getDisplayPropertiesForGetResolution(); + $properties['UserSettings.getConfiguration'] = $this->getDisplayPropertiesForGetConfiguration(); + $properties['UserSettings.getOS'] = $this->getDisplayPropertiesForGetOS(); + $properties['UserSettings.getOSFamily'] = $this->getDisplayPropertiesForGetOSFamily(); + $properties['UserSettings.getBrowserVersion'] = $this->getDisplayPropertiesForGetBrowserVersion(); + $properties['UserSettings.getBrowser'] = $this->getDisplayPropertiesForGetBrowser(); + $properties['UserSettings.getBrowserType'] = $this->getDisplayPropertiesForGetBrowserType(); + $properties['UserSettings.getWideScreen'] = $this->getDisplayPropertiesForGetWideScreen(); + $properties['UserSettings.getMobileVsDesktop'] = $this->getDisplayPropertiesForGetMobileVsDesktop(); + $properties['UserSettings.getPlugin'] = $this->getDisplayPropertiesForGetPlugin(); + $properties['UserSettings.getLanguage'] = $this->getDisplayPropertiesForGetLanguage(); + } + + private function getDisplayPropertiesForGetResolution() + { + return array_merge($this->getBasicUserSettingsDisplayProperties(), array( 'translations' => array('label' => Piwik_Translate('UserSettings_ColumnResolution')) )); - - $properties['UserSettings.getConfiguration'] = array_merge($basicUserSettingsProperties, array( + } + + private function getDisplayPropertiesForGetConfiguration() + { + return array_merge($this->getBasicUserSettingsDisplayProperties(), array( 'filter_limit' => 3, 'translations' => array('label' => Piwik_Translate('UserSettings_ColumnConfiguration')) )); - - $properties['UserSettings.getOS'] = array_merge($basicUserSettingsProperties, array( + } + + private function getDisplayPropertiesForGetOS() + { + return array_merge($this->getBasicUserSettingsDisplayProperties(), array( 'translations' => array('label' => Piwik_Translate('UserSettings_ColumnOperatingSystem')), 'title' => Piwik_Translate('UserSettings_OperatingSystems'), - 'relatedReports' => $osRelatedReports + 'related_reports' => $this->getOsRelatedReports() )); - - $properties['UserSettings.getOSFamily'] = array_merge($basicUserSettingsProperties, array( + } + + private function getDisplayPropertiesForGetOSFamily() + { + return array_merge($this->getBasicUserSettingsDisplayProperties(), array( 'translations' => array('label' => Piwik_Translate('UserSettings_OperatingSystemFamily')), 'title' => Piwik_Translate('UserSettings_OperatingSystemFamily'), - 'relatedReports' => $osRelatedReports + 'related_reports' => $this->getOsRelatedReports() )); - - $properties['UserSettings.getBrowserVersion'] = array_merge($basicUserSettingsProperties, array( + } + + private function getDisplayPropertiesForGetBrowserVersion() + { + $result = array_merge($this->getBasicUserSettingsDisplayProperties(), array( 'translations' => array('label' => Piwik_Translate('UserSettings_ColumnBrowserVersion')), - 'graph_limit' => 7, 'title' => Piwik_Translate('UserSettings_ColumnBrowserVersion'), - 'relatedReports' => $browserRelatedReports + 'related_reports' => $this->getBrowserRelatedReports() )); - - $properties['UserSettings.getBrowser'] = array_merge($basicUserSettingsProperties, array( + $result['visualization_properties']['JqplotGraph']['max_graph_elements'] = 7; + return $result; + } + + private function getDisplayPropertiesForGetBrowser() + { + $result = array_merge($this->getBasicUserSettingsDisplayProperties(), array( 'translations' => array('label' => Piwik_Translate('UserSettings_ColumnBrowser')), - 'graph_limit' => 7, 'title' => Piwik_Translate('UserSettings_Browsers'), - 'relatedReports' => $browserRelatedReports + 'related_reports' => $this->getBrowserRelatedReports() )); - - $properties['UserSettings.getBrowserType'] = array_merge($basicUserSettingsProperties, array( + $result['visualization_properties']['JqplotGraph']['max_graph_elements'] = 7; + return $result; + } + + private function getDisplayPropertiesForGetBrowserType() + { + return array_merge($this->getBasicUserSettingsDisplayProperties(), array( 'translations' => array('label' => Piwik_Translate('UserSettings_ColumnBrowserFamily')), 'show_offset_information' => false, 'show_pagination_control' => false, 'default_view_type' => 'graphPie', )); - - $properties['UserSettings.getWideScreen'] = array_merge($basicUserSettingsProperties, array( + } + + private function getDisplayPropertiesForGetWideScreen() + { + return array_merge($this->getBasicUserSettingsDisplayProperties(), array( 'translations' => array('label' => Piwik_Translate('UserSettings_ColumnTypeOfScreen')), 'show_offset_information' => false, 'show_pagination_control' => false, 'title' => Piwik_Translate('UserSettings_ColumnTypeOfScreen'), - 'relatedReports' => $wideScreenDeviceTypeRelatedReports + 'related_reports' => $this->getWideScreenDeviceTypeRelatedReports() )); - - $properties['UserSettings.getMobileVsDesktop'] = array_merge($basicUserSettingsProperties, array( + } + + private function getDisplayPropertiesForGetMobileVsDesktop() + { + return array_merge($this->getBasicUserSettingsDisplayProperties(), array( 'translations' => array('label' => Piwik_Translate('UserSettings_MobileVsDesktop')), 'title' => Piwik_Translate('UserSettings_MobileVsDesktop'), - 'relatedReports' => $wideScreenDeviceTypeRelatedReports + 'related_reports' => $this->getWideScreenDeviceTypeRelatedReports() )); - - $properties['UserSettings.getPlugin'] = array_merge($basicUserSettingsProperties, array( + } + + private function getDisplayPropertiesForGetPlugin() + { + return array_merge($this->getBasicUserSettingsDisplayProperties(), array( 'translations' => array( 'label' => Piwik_Translate('UserSettings_ColumnPlugin'), 'nb_visits_percentage' => @@ -267,18 +291,58 @@ class Piwik_UserSettings extends Plugin 'filter_limit' => 10, 'show_footer_message' => Piwik_Translate('UserSettings_PluginDetectionDoesNotWorkInIE'), )); - - $properties['UserSettings.getLanguage'] = array( + } + + private function getDisplayPropertiesForGetLanguage() + { + return array( 'translations' => array('label' => Piwik_Translate('General_Language')), 'filter_sort_column' => 'nb_visits', 'filter_sort_order' => 'desc', 'show_search' => false, - 'filter_limit' => false, 'columns_to_display' => array('label', 'nb_visits'), 'show_exclude_low_population' => false, ); } + private function getWideScreenDeviceTypeRelatedReports() + { + return array( + 'UserSettings.getMobileVsDesktop' => Piwik_Translate('UserSettings_MobileVsDesktop'), + 'UserSettings.getWideScreen' => Piwik_Translate('UserSettings_ColumnTypeOfScreen') + ); + } + + private function getBrowserRelatedReports() + { + return array( + 'UserSettings.getBrowser' => Piwik_Translate('UserSettings_Browsers'), + 'UserSettings.getBrowserVersion' => Piwik_Translate('UserSettings_ColumnBrowserVersion') + ); + } + + private function getOsRelatedReports() + { + return array( + 'UserSettings.getOSFamily' => Piwik_Translate('UserSettings_OperatingSystemFamily'), + 'UserSettings.getOS' => Piwik_Translate('UserSettings_OperatingSystems') + ); + } + + private function getBasicUserSettingsDisplayProperties() + { + return array( + 'show_search' => false, + 'show_exclude_low_population' => false, + 'filter_limit' => 5, + 'visualization_properties' => array( + 'JqplotGraph' => array( + 'max_graph_elements' => 5 + ) + ) + ); + } + /** * Registers reports metadata * diff --git a/plugins/VisitTime/VisitTime.php b/plugins/VisitTime/VisitTime.php index 9b652577d3..3273daf3ea 100644 --- a/plugins/VisitTime/VisitTime.php +++ b/plugins/VisitTime/VisitTime.php @@ -131,34 +131,44 @@ class Piwik_VisitTime extends Plugin $properties['VisitTime.getVisitInformationPerServerTime'] = array_merge($commonProperties, array( 'filter_limit' => 24, - 'graph_limit' => null, 'show_goals' => true, 'translations' => array('label' => Piwik_Translate('VisitTime_ColumnServerTime')), - - // custom parameter - 'hideFutureHoursWhenToday' => 1, + 'request_parameters_to_modify' => array('hideFutureHoursWhenToday' => 1), + 'visualization_properties' => array( + 'JqplotGraph' => array( + 'max_graph_elements' => false, + ) + ) )); $properties['VisitTime.getVisitInformationPerLocalTime'] = array_merge($commonProperties, array( 'filter_limit' => 24, - 'graph_limit' => null, 'title' => Piwik_Translate('VisitTime_ColumnLocalTime'), 'translations' => array('label' => Piwik_Translate('VisitTime_LocalTime')), + 'visualization_properties' => array( + 'JqplotGraph' => array( + 'max_graph_elements' => false, + ) + ) )); $properties['VisitTime.getByDayOfWeek'] = array_merge($commonProperties, array( 'filter_limit' => 7, - 'graph_limit' => null, 'enable_sort' => false, - 'show_all_ticks' => true, 'show_footer_message' => Piwik_Translate('General_ReportGeneratedFrom', self::getDateRangeForFooterMessage()), 'translations' => array('label' => Piwik_Translate('VisitTime_DayOfWeek')), + 'visualization_properties' => array( + 'JqplotGraph' => array( + 'show_all_ticks' => true, + 'max_graph_elements' => false, + ) + ) )); // add the visits by day of week as a related report, if the current period is not 'day' if (Common::getRequestVar('period', 'day') != 'day') { - $properties['VisitTime.getVisitInformationPerLocalTime']['relatedReports'] = array( + $properties['VisitTime.getVisitInformationPerLocalTime']['related_reports'] = array( 'VisitTime.getByDayOfWeek' => Piwik_Translate('VisitTime_VisitsByDayOfWeek') ); } diff --git a/plugins/VisitorInterest/VisitorInterest.php b/plugins/VisitorInterest/VisitorInterest.php index a656e73804..6ea2c4eb69 100644 --- a/plugins/VisitorInterest/VisitorInterest.php +++ b/plugins/VisitorInterest/VisitorInterest.php @@ -166,13 +166,17 @@ class Piwik_VisitorInterest extends Plugin 'filter_sort_column' => 'label', 'filter_sort_order' => 'asc', 'translations' => array('label' => Piwik_Translate('VisitorInterest_ColumnVisitDuration')), - 'graph_limit' => 10, 'enable_sort' => false, 'show_exclude_low_population' => false, 'show_offset_information' => false, 'show_pagination_control' => false, 'show_search' => false, 'show_table_all_columns' => false, + 'visualization_properties' => array( + 'JqplotGraph' => array( + 'max_graph_elements' => 10 + ) + ) ); } @@ -183,13 +187,17 @@ class Piwik_VisitorInterest extends Plugin 'filter_sort_column' => 'label', 'filter_sort_order' => 'asc', 'translations' => array('label' => Piwik_Translate('VisitorInterest_ColumnPagesPerVisit')), - 'graph_limit' => 10, 'enable_sort' => false, 'show_exclude_low_population' => false, 'show_offset_information' => false, 'show_pagination_control' => false, 'show_search' => false, 'show_table_all_columns' => false, + 'visualization_properties' => array( + 'JqplotGraph' => array( + 'max_graph_elements' => 10 + ) + ) ); } diff --git a/tests/PHPUnit/Integration/FlattenReportsTest.php b/tests/PHPUnit/Integration/FlattenReportsTest.php index 48ecdad576..19d9ed1414 100644 --- a/tests/PHPUnit/Integration/FlattenReportsTest.php +++ b/tests/PHPUnit/Integration/FlattenReportsTest.php @@ -88,6 +88,14 @@ class Test_Piwik_Integration_FlattenReports extends IntegrationTestCase 'testSuffix' => '_expandedSubtable', 'otherRequestParameters' => array('expanded' => '1'))); + // test expanded=1 & depth=1 + $return[] = array('Actions.getPageUrls', array('idSite' => $idSite, + 'date' => $dateTime, + 'periods' => array('week'), + 'testSuffix' => '_expandedWithDepth', + 'otherRequestParameters' => array('expanded' => '1', + 'depth' => '1'))); + // test flat=1 w/ filter_pattern_recursive $return[] = array('Actions.getPageUrls', array('idSite' => $idSite, 'date' => $dateTime, diff --git a/tests/PHPUnit/Integration/expected/test_FlattenReports_expandedWithDepth__Actions.getPageUrls_week.xml b/tests/PHPUnit/Integration/expected/test_FlattenReports_expandedWithDepth__Actions.getPageUrls_week.xml new file mode 100644 index 0000000000..3f3d4a7509 --- /dev/null +++ b/tests/PHPUnit/Integration/expected/test_FlattenReports_expandedWithDepth__Actions.getPageUrls_week.xml @@ -0,0 +1,155 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <label>dir1</label> + <nb_visits>6</nb_visits> + <nb_hits>6</nb_hits> + <sum_time_spent>0</sum_time_spent> + <nb_hits_with_time_generation>6</nb_hits_with_time_generation> + <min_time_generation>0.1</min_time_generation> + <max_time_generation>0.6</max_time_generation> + <entry_nb_visits>2</entry_nb_visits> + <entry_nb_actions>6</entry_nb_actions> + <entry_sum_visit_length>2</entry_sum_visit_length> + <entry_bounce_count>0</entry_bounce_count> + <exit_nb_visits>2</exit_nb_visits> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>33%</exit_rate> + <avg_time_generation>0.3</avg_time_generation> + <subtable> + <row> + <label>sub</label> + <nb_visits>6</nb_visits> + <nb_hits>6</nb_hits> + <sum_time_spent>0</sum_time_spent> + <nb_hits_with_time_generation>6</nb_hits_with_time_generation> + <min_time_generation>0.1</min_time_generation> + <max_time_generation>0.6</max_time_generation> + <entry_nb_visits>2</entry_nb_visits> + <entry_nb_actions>6</entry_nb_actions> + <entry_sum_visit_length>2</entry_sum_visit_length> + <entry_bounce_count>0</entry_bounce_count> + <exit_nb_visits>2</exit_nb_visits> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>33%</exit_rate> + <avg_time_generation>0.3</avg_time_generation> + </row> + </subtable> + </row> + <row> + <label>dir2</label> + <nb_visits>6</nb_visits> + <nb_hits>6</nb_hits> + <sum_time_spent>0</sum_time_spent> + <nb_hits_with_time_generation>6</nb_hits_with_time_generation> + <min_time_generation>0.2</min_time_generation> + <max_time_generation>1.2</max_time_generation> + <entry_nb_visits>2</entry_nb_visits> + <entry_nb_actions>6</entry_nb_actions> + <entry_sum_visit_length>2</entry_sum_visit_length> + <entry_bounce_count>0</entry_bounce_count> + <exit_nb_visits>2</exit_nb_visits> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>33%</exit_rate> + <avg_time_generation>0.6</avg_time_generation> + <subtable> + <row> + <label>sub</label> + <nb_visits>6</nb_visits> + <nb_hits>6</nb_hits> + <sum_time_spent>0</sum_time_spent> + <nb_hits_with_time_generation>6</nb_hits_with_time_generation> + <min_time_generation>0.2</min_time_generation> + <max_time_generation>1.2</max_time_generation> + <entry_nb_visits>2</entry_nb_visits> + <entry_nb_actions>6</entry_nb_actions> + <entry_sum_visit_length>2</entry_sum_visit_length> + <entry_bounce_count>0</entry_bounce_count> + <exit_nb_visits>2</exit_nb_visits> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>33%</exit_rate> + <avg_time_generation>0.6</avg_time_generation> + </row> + </subtable> + </row> + <row> + <label>dir3</label> + <nb_visits>6</nb_visits> + <nb_hits>6</nb_hits> + <sum_time_spent>0</sum_time_spent> + <nb_hits_with_time_generation>6</nb_hits_with_time_generation> + <min_time_generation>0.3</min_time_generation> + <max_time_generation>1.8</max_time_generation> + <entry_nb_visits>2</entry_nb_visits> + <entry_nb_actions>6</entry_nb_actions> + <entry_sum_visit_length>2</entry_sum_visit_length> + <entry_bounce_count>0</entry_bounce_count> + <exit_nb_visits>2</exit_nb_visits> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>33%</exit_rate> + <avg_time_generation>0.9</avg_time_generation> + <subtable> + <row> + <label>sub</label> + <nb_visits>6</nb_visits> + <nb_hits>6</nb_hits> + <sum_time_spent>0</sum_time_spent> + <nb_hits_with_time_generation>6</nb_hits_with_time_generation> + <min_time_generation>0.3</min_time_generation> + <max_time_generation>1.8</max_time_generation> + <entry_nb_visits>2</entry_nb_visits> + <entry_nb_actions>6</entry_nb_actions> + <entry_sum_visit_length>2</entry_sum_visit_length> + <entry_bounce_count>0</entry_bounce_count> + <exit_nb_visits>2</exit_nb_visits> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>33%</exit_rate> + <avg_time_generation>0.9</avg_time_generation> + </row> + </subtable> + </row> + <row> + <label>sub</label> + <nb_visits>1</nb_visits> + <nb_hits>1</nb_hits> + <sum_time_spent>0</sum_time_spent> + <nb_hits_with_time_generation>0</nb_hits_with_time_generation> + <min_time_generation /> + <max_time_generation>0</max_time_generation> + <entry_nb_visits>1</entry_nb_visits> + <entry_nb_actions>1</entry_nb_actions> + <entry_sum_visit_length>0</entry_sum_visit_length> + <entry_bounce_count>1</entry_bounce_count> + <exit_nb_visits>1</exit_nb_visits> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>100%</bounce_rate> + <exit_rate>100%</exit_rate> + <avg_time_generation>0</avg_time_generation> + <subtable> + <row> + <label>dir</label> + <nb_visits>1</nb_visits> + <nb_hits>1</nb_hits> + <sum_time_spent>0</sum_time_spent> + <nb_hits_with_time_generation>0</nb_hits_with_time_generation> + <min_time_generation /> + <max_time_generation>0</max_time_generation> + <entry_nb_visits>1</entry_nb_visits> + <entry_nb_actions>1</entry_nb_actions> + <entry_sum_visit_length>0</entry_sum_visit_length> + <entry_bounce_count>1</entry_bounce_count> + <exit_nb_visits>1</exit_nb_visits> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>100%</bounce_rate> + <exit_rate>100%</exit_rate> + <avg_time_generation>0</avg_time_generation> + </row> + </subtable> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/IntegrationTestCase.php b/tests/PHPUnit/IntegrationTestCase.php index 0fdb16c32d..a9f183652e 100755 --- a/tests/PHPUnit/IntegrationTestCase.php +++ b/tests/PHPUnit/IntegrationTestCase.php @@ -243,9 +243,6 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase $_GET = $_REQUEST = array(); Translate::getInstance()->unloadEnglishTranslation(); - - // re-enable tag cloud shuffling - Cloud::$debugDisableShuffle = true; } public function setUp() diff --git a/tests/PHPUnit/TestingEnvironment.php b/tests/PHPUnit/TestingEnvironment.php index 6e015ab681..99d2822ef6 100644 --- a/tests/PHPUnit/TestingEnvironment.php +++ b/tests/PHPUnit/TestingEnvironment.php @@ -20,5 +20,8 @@ class Piwik_TestingEnvironment Piwik_AddAction('Config.createConfigSingleton', function($config) { $config->setTestEnvironment(); }); + Piwik_AddAction('FrontController.dispatch', function() { + \Piwik\Plugins\CoreVisualizations\Visualizations\Cloud::$debugDisableShuffle = true; + }); } }
\ No newline at end of file diff --git a/tests/PHPUnit/UI/UIIntegrationTest.php b/tests/PHPUnit/UI/UIIntegrationTest.php index 066f8502e0..4571227247 100644 --- a/tests/PHPUnit/UI/UIIntegrationTest.php +++ b/tests/PHPUnit/UI/UIIntegrationTest.php @@ -21,6 +21,7 @@ use Piwik\AssetManager; class Test_Piwik_Integration_UIIntegrationTest extends IntegrationTestCase { const IMAGE_TYPE = 'png'; + const CUTYCAPT_DELAY = 1000; public static $fixture = null; // initialized below class definition private static $useXvfb = false; @@ -203,14 +204,14 @@ class Test_Piwik_Integration_UIIntegrationTest extends IntegrationTestCase $this->runCutyCapt($urlQuery, $processedScreenshotPath); // compare processed w/ expected - $this->compareScreenshot($name, $expectedScreenshotPath, $processedScreenshotPath); + $this->compareScreenshot($name, $expectedScreenshotPath, $processedScreenshotPath, $urlQuery); } private function runCutyCapt($urlQuery, $processedPath) { $url = self::getProxyUrl() . $urlQuery; - $cmd = "cutycapt --url=\"$url\" --out=\"$processedPath\" --min-width=1366 --delay=1000 2>&1"; + $cmd = "cutycapt --url=\"$url\" --out=\"$processedPath\" --min-width=1366 --delay=".self::CUTYCAPT_DELAY." 2>&1"; if (self::$useXvfb) { $cmd = 'xvfb-run --server-args="-screen 0, 1024x768x24" ' . $cmd; } @@ -224,7 +225,7 @@ class Test_Piwik_Integration_UIIntegrationTest extends IntegrationTestCase return $output; } - private function compareScreenshot($name, $expectedPath, $processedPath) + private function compareScreenshot($name, $expectedPath, $processedPath, $urlQuery) { $processed = file_get_contents($processedPath); @@ -233,6 +234,9 @@ class Test_Piwik_Integration_UIIntegrationTest extends IntegrationTestCase } $expected = file_get_contents($expectedPath); + if ($expected != $processed) { + echo "\nFail: '$processedPath' for '$urlQuery'\n"; + } $this->assertTrue($expected == $processed, "screenshot compare failed for '$processedPath'"); } @@ -260,5 +264,4 @@ class Test_Piwik_Integration_UIIntegrationTest extends IntegrationTestCase } } -Test_Piwik_Integration_UIIntegrationTest::$fixture = new Test_Piwik_Fixture_ManySitesImportedLogsWithXssAttempts(); - +Test_Piwik_Integration_UIIntegrationTest::$fixture = new Test_Piwik_Fixture_ManySitesImportedLogsWithXssAttempts();
\ No newline at end of file diff --git a/tests/PHPUnit/proxy/index.php b/tests/PHPUnit/proxy/index.php index 12f5e0b2a9..14c0d7593d 100644 --- a/tests/PHPUnit/proxy/index.php +++ b/tests/PHPUnit/proxy/index.php @@ -6,7 +6,6 @@ // make sure the test environment is loaded use Piwik\Tracker\Cache; -use Piwik\Visualization\Cloud; // Wrapping the request inside ob_start() calls to ensure that the Test // calling us waits for the full request to process before unblocking @@ -23,8 +22,6 @@ require_once realpath(dirname(__FILE__)) . '/../../../core/functions.php'; require_once realpath(dirname(__FILE__)) . "/../../../tests/PHPUnit/TestingEnvironment.php"; Piwik_TestingEnvironment::addHooks(); -Cloud::$debugDisableShuffle = true; - \Piwik\Tracker::setTestEnvironment(); Cache::deleteTrackerCache(); |