diff options
author | Benaka Moorthi <benaka.moorthi@gmail.com> | 2013-08-05 02:46:23 +0400 |
---|---|---|
committer | Benaka Moorthi <benaka.moorthi@gmail.com> | 2013-08-05 02:46:23 +0400 |
commit | 2615404eab2e96048583caccf86248af2dd90d9b (patch) | |
tree | 0d85d6b4dcc2bb61bd244d29b99aaf2378fbc131 /core | |
parent | cf8d0a51591e39ecf8aa8f4c09b5672ec2d312cd (diff) |
Refs #4040, #4041, move visualization only view properties to separate visualizations, changed JavaScript properties to 'client side parameters' and 'overridable properties' to 'client side properties', changed keep_summary_row from a JavaScript property to a client side property, allowed visualization properties to be inherited, fixed system that allowed visualizations to set custom defaults for property values, modified behavior of datatable_css_class viewdatatable property, allow view properties to be customized in metadata based on the visualization used, and tweaks to UI Integration tests.
Diffstat (limited to 'core')
-rw-r--r-- | core/Controller.php | 3 | ||||
-rw-r--r-- | core/DataTableVisualization.php | 38 | ||||
-rw-r--r-- | core/JqplotDataGenerator.php | 20 | ||||
-rw-r--r-- | core/Twig.php | 9 | ||||
-rw-r--r-- | core/ViewDataTable.php | 161 | ||||
-rw-r--r-- | core/ViewDataTable/Properties.php | 223 | ||||
-rw-r--r-- | core/ViewDataTable/Sparkline.php | 2 | ||||
-rw-r--r-- | core/ViewDataTable/VisualizationPropertiesProxy.php | 30 | ||||
-rw-r--r-- | core/Visualization/Cloud.php | 5 | ||||
-rw-r--r-- | core/Visualization/HtmlTable.php | 92 | ||||
-rw-r--r-- | core/Visualization/JqplotGraph.php | 139 | ||||
-rw-r--r-- | core/Visualization/JqplotGraph/Bar.php | 6 | ||||
-rw-r--r-- | core/Visualization/JqplotGraph/Evolution.php | 17 | ||||
-rw-r--r-- | core/Visualization/JqplotGraph/Pie.php | 8 |
14 files changed, 451 insertions, 302 deletions
diff --git a/core/Controller.php b/core/Controller.php index 5aac70c2e2..1e7b64fcea 100644 --- a/core/Controller.php +++ b/core/Controller.php @@ -219,7 +219,8 @@ 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) { diff --git a/core/DataTableVisualization.php b/core/DataTableVisualization.php index ca41f65377..fe3b184e90 100644 --- a/core/DataTableVisualization.php +++ b/core/DataTableVisualization.php @@ -26,10 +26,10 @@ abstract class DataTableVisualization /** * TODO */ - public static function getJavaScriptProperties() + public static function getClientSideParameters() { - if (isset(static::$javaScriptProperties)) { - return static::$javaScriptProperties; + if (isset(static::$clientSideParameters)) { + return static::$clientSideParameters; } else { return array(); } @@ -38,10 +38,10 @@ abstract class DataTableVisualization /** * TODO */ - public static function getOverridableProperties() + public static function getClientSideProperties() { - if (isset(static::$overridableProperties)) { - return static::$overridableProperties; + if (isset(static::$clientSideProperties)) { + return static::$clientSideProperties; } else { return array(); } @@ -58,4 +58,28 @@ abstract class DataTableVisualization return Piwik::getUnnamespacedClassName($this); } } -} + + /** + * TODO + */ + 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); + } + + /** + * TODO + */ + public static function getVisualizationIdsWithInheritance($klass) + { + $klasses = self::getVisualizationClassLineage($klass); + return array_map(array('Piwik\\Piwik', 'getUnnamespacedClassName'), $klasses); + } +}
\ No newline at end of file diff --git a/core/JqplotDataGenerator.php b/core/JqplotDataGenerator.php index 039370b251..e8bd9892eb 100644 --- a/core/JqplotDataGenerator.php +++ b/core/JqplotDataGenerator.php @@ -82,8 +82,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 +95,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 +136,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 +187,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 +201,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/Twig.php b/core/Twig.php index 5a316c65ce..f65825065b 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 b50c1ed517..1d348cffec 100644 --- a/core/ViewDataTable.php +++ b/core/ViewDataTable.php @@ -62,9 +62,8 @@ class ViewDataTable { /** * TODO - * TODO: change to private */ - protected $visualizationClass; + private $visualizationClass; /** * Cache for getAllReportDisplayProperties result. @@ -129,7 +128,7 @@ class ViewDataTable { $this->visualizationClass = $visualizationClass; - $this->viewProperties['visualization_properties'] = new VisualizationPropertiesProxy(null); + $this->viewProperties['visualization_properties'] = new VisualizationPropertiesProxy($visualizationClass); $this->viewProperties['datatable_template'] = '@CoreHome/_dataTable'; $this->viewProperties['show_goals'] = false; $this->viewProperties['show_ecommerce'] = false; @@ -138,7 +137,6 @@ class ViewDataTable $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; @@ -175,11 +173,8 @@ class ViewDataTable $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['datatable_css_class'] = false; $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); @@ -187,6 +182,8 @@ class ViewDataTable $this->viewProperties['columns_to_display'] = Piwik::getArrayFromApiParameter($columns); array_unshift($this->viewProperties['columns_to_display'], 'label'); } + + $this->setDefaultPropertiesForVisualization(); } /** @@ -219,6 +216,14 @@ class ViewDataTable } /** + * Hack to allow property access in Twig (w/ property name checking). + */ + public function __call($name, $arguments) + { + return $this->$name; + } + + /** * TODO */ public function main() @@ -250,7 +255,7 @@ class ViewDataTable * * @return string */ - protected function getViewDataTableId() + public function getViewDataTableId() { $klass = $this->visualizationClass; return $klass::getViewDataTableId($this); @@ -288,19 +293,19 @@ class ViewDataTable $type = Common::getRequestVar('viewDataTable', $defaultType, 'string'); switch ($type) { case 'cloud': - $result = new ViewDataTable('\\Piwik\\Visualization\\JqplotGraph\\Cloud'); + $result = new ViewDataTable('Piwik\\Visualization\\Cloud'); break; case 'graphPie': - $result = new ViewDataTable('\\Piwik\\Visualization\\JqplotGraph\\Pie'); + $result = new ViewDataTable('Piwik\\Visualization\\JqplotGraph\\Pie'); break; case 'graphVerticalBar': - $result = new ViewDataTable('\\Piwik\\Visualization\\JqplotGraph\\Bar'); + $result = new ViewDataTable('Piwik\\Visualization\\JqplotGraph\\Bar'); break; case 'graphEvolution': - $result = new ViewDataTable('\\Piwik\\Visualization\\JqplotGraph\\Evolution'); + $result = new ViewDataTable('Piwik\\Visualization\\JqplotGraph\\Evolution'); break; case 'sparkline': @@ -308,18 +313,18 @@ class ViewDataTable 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; + $result = new ViewDataTable('Piwik\\Visualization\\HtmlTable'); + $result->visualization_properties->show_extra_columns = true; break; case 'tableGoals': // for backwards compatibility - $result = new ViewDataTable('\\Piwik\\Visualization\\HtmlTable'); - $result->show_goals_columns = true; + $result = new ViewDataTable('Piwik\\Visualization\\HtmlTable'); + $result->visualization_properties->show_goals_columns = true; break; case 'table': default: - $result = new ViewDataTable('\\Piwik\\Visualization\\HtmlTable'); + $result = new ViewDataTable('Piwik\\Visualization\\HtmlTable'); break; } @@ -338,11 +343,11 @@ class ViewDataTable } /** - * 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 +355,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 +365,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 +382,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,7 +399,7 @@ class ViewDataTable if ($this->visualizationClass) { $klass = $this->visualizationClass; - $result = array_merge($result, $klass::getJavaScriptProperties()); + $result = array_merge($result, $klass::getClientSideParameters()); } return $result; @@ -437,7 +440,7 @@ class ViewDataTable } $queryParams = Url::getArrayFromCurrentQueryString(); - foreach ($this->getOverridableProperties() as $name) { + foreach ($this->getClientSideProperties() as $name) { if (isset($queryParams[$name])) { $this->setViewProperty($name, $queryParams[$name]); } @@ -463,21 +466,6 @@ class ViewDataTable } /** - * 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. * @@ -579,10 +567,40 @@ class ViewDataTable || $name == 'filters' ) { $this->viewProperties[$name] = array_merge($this->viewProperties[$name], $value); - } else if ($name == 'relatedReports') { + } else if ($name == 'relatedReports') { // 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; + } + } } } @@ -899,7 +917,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 +980,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() * @@ -1173,14 +1205,6 @@ 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; } @@ -1196,7 +1220,8 @@ class ViewDataTable } $view->visualization = $visualization; - + $view->visualizationCssClass = $this->getDefaultDataTableCssClass(); + if (!$this->dataTable === null) { $view->dataTable = null; } else { @@ -1208,7 +1233,8 @@ class ViewDataTable $view->deleteReportsOlderThan = Piwik_GetOption('delete_reports_older_than'); } $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet(); - $view->properties = $this->viewProperties; + $view->clientSidePropertiesToSet = $this->getClientSidePropertiesToSet(); + $view->properties = $this->viewProperties; // TODO: should be $this. need to move non-view properties from the class return $view; } @@ -1217,22 +1243,17 @@ class ViewDataTable return 'dataTableViz' . Piwik::getUnnamespacedClassName($this->visualizationClass); } - /** - * Sets view properties if they have not been set already. - */ - public function defaultPropertiesTo($defaultValues) + private function setDefaultPropertiesForVisualization() { - // 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; - } + if ($this->visualizationClass === null) { + return; + } + + $visualizationClass = $this->visualizationClass; + $defaultProperties = $visualizationClass::getDefaultPropertyValues(); + + foreach ($defaultProperties as $name => $value) { + $this->setViewProperty($name, $value); } } }
\ No newline at end of file diff --git a/core/ViewDataTable/Properties.php b/core/ViewDataTable/Properties.php index 8d55d5a926..aab499a6a9 100644 --- a/core/ViewDataTable/Properties.php +++ b/core/ViewDataTable/Properties.php @@ -25,6 +25,14 @@ use ReflectionClass; 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 +62,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,16 +119,6 @@ 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. @@ -150,13 +141,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'; @@ -228,13 +212,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 +222,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 +310,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 +322,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 +329,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 +358,23 @@ class Properties const SUBTABLE_CONTROLLER_ACTION = 'subtable_controller_action'; /** - * TODO: specific only to jqplot graphs. change to just 'width'? - */ - const GRAPH_WIDTH = 'graph_width'; - - /** - * TODO: same as GRAPH_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 GRAPH_HEIGHT = 'graph_height'; + const SHOW_PAGINATION_CONTROL = 'show_pagination_control'; /** - * TODO: only valid for jqplot graphs. + * Controls whether offset information (ie, '5-10 of 20') is shown under the datatable. + * + * @see self::SHOW_PAGINATION_CONTROL */ - const SHOW_SERIES_PICKER = 'show_series_picker'; + const SHOW_OFFSET_INFORMATION = 'show_offset_information'; /** * 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 +383,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 +437,32 @@ 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'."); } } + + private static function getFlippedClassConstantMap($klass) + { + $klass = new ReflectionClass($klass); + $constants = $klass->getConstants(); + unset($constants['ID']); + return array_flip($constants); + } }
\ No newline at end of file diff --git a/core/ViewDataTable/Sparkline.php b/core/ViewDataTable/Sparkline.php index 02988a86dd..fe7198d14e 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'; } diff --git a/core/ViewDataTable/VisualizationPropertiesProxy.php b/core/ViewDataTable/VisualizationPropertiesProxy.php index c6ef3f079e..6a39bf8a4d 100644 --- a/core/ViewDataTable/VisualizationPropertiesProxy.php +++ b/core/ViewDataTable/VisualizationPropertiesProxy.php @@ -20,11 +20,11 @@ use Piwik\ViewDataTable\Properties; class VisualizationPropertiesProxy { /** - * The visualization instance. + * The visualization class name. * - * @var array + * @var string */ - private $visualization; + private $visualizationClass; /** * Stores visualization properties. @@ -36,11 +36,19 @@ class VisualizationPropertiesProxy /** * Constructor. * - * @param \Piwik\Visualization\ $visualization The visualization to get/set properties of. + * @param string $visualizationClass The visualization class to get/set properties of. + */ + public function __construct($visualizationClass) + { + $this->visualizationClass = $visualizationClass; + } + + /** + * Hack to allow property access in Twig (w/ property name checking). */ - public function __construct($visualization) + public function __call($name, $arguments) { - $this->visualization = $visualization; + return $this->$name; } /** @@ -52,7 +60,10 @@ class VisualizationPropertiesProxy */ public function &__get($name) { - Properties::checkValidVisualizationProperty($this->visualization, $name); + if ($this->visualizationClass !== null) { + Properties::checkValidVisualizationProperty($this->visualizationClass, $name); + } + return $this->visualizationProperties[$name]; } @@ -66,7 +77,10 @@ class VisualizationPropertiesProxy */ public function __set($name, $value) { - Properties::checkValidVisualizationProperty($this->visualization, $name); + if ($this->visualizationClass !== null) { + Properties::checkValidVisualizationProperty($this->visualizationClass, $name); + } + return $this->visualizationProperties[$name] = $value; } }
\ No newline at end of file diff --git a/core/Visualization/Cloud.php b/core/Visualization/Cloud.php index 47fb4cbe04..2476e7ab1f 100644 --- a/core/Visualization/Cloud.php +++ b/core/Visualization/Cloud.php @@ -34,11 +34,6 @@ class Cloud extends DataTableVisualization protected $wordsArray = array(); public $truncatingLimit = 50; - public function __construct($view) - { - $view->defaultPropertiesTo($this->getDefaultPropertyValues()); - } - public static function getDefaultPropertyValues() { return array( diff --git a/core/Visualization/HtmlTable.php b/core/Visualization/HtmlTable.php index 04519ae922..208c16844b 100644 --- a/core/Visualization/HtmlTable.php +++ b/core/Visualization/HtmlTable.php @@ -28,16 +28,67 @@ class HtmlTable extends DataTableVisualization { const ID = 'table'; + /** + * 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'; + // TODO: names for these types of properties are inappropriate. JS properties are actually properties // that get passed for each request. Overridable properties are properties that do not get passed on, // but are visible to client side JS. (change to clientSideProperties & clientSideParameters) - static public $javaScriptProperties = array( - 'search_recursive' + static public $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 +96,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()); } /** @@ -73,26 +128,29 @@ class HtmlTable extends DataTableVisualization public static function getViewDataTableId($view) // TODO: shouldn't need this override { - if ($view->show_extra_columns) { + if ($view->visualization_properties->show_extra_columns) { return 'tableAllColumns'; - } else if ($view->show_goals_columns) { + } else if ($view->visualization_properties->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 +211,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/core/Visualization/JqplotGraph.php b/core/Visualization/JqplotGraph.php index caa125f702..b3b6227321 100644 --- a/core/Visualization/JqplotGraph.php +++ b/core/Visualization/JqplotGraph.php @@ -26,7 +26,75 @@ class JqplotGraph extends DataTableVisualization const DEFAULT_GRAPH_HEIGHT = 250; /** - * TODO + * 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) { @@ -37,43 +105,15 @@ class JqplotGraph extends DataTableVisualization // 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->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'); } - $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; @@ -86,15 +126,46 @@ class JqplotGraph extends DataTableVisualization $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') { + if (Common::getRequestVar('period', false) == 'day') { // TODO: should depend on columns datatable has. $selectableColumns[] = 'nb_uniq_visitors'; } - $result['selectable_columns'] = $selectableColumns; - return $result; + 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_mach_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, + ) + ) + ); } /** diff --git a/core/Visualization/JqplotGraph/Bar.php b/core/Visualization/JqplotGraph/Bar.php index 72de0beb8a..76c814dcef 100644 --- a/core/Visualization/JqplotGraph/Bar.php +++ b/core/Visualization/JqplotGraph/Bar.php @@ -21,10 +21,10 @@ class Bar extends JqplotGraph { const ID = 'graphVerticalBar'; - protected function getDefaultPropertyValues($view) + public static function getDefaultPropertyValues() { - $result = parent::getDefaultPropertyValues($view); - $result['graph_limit'] = 6; + $result = parent::getDefaultPropertyValues(); + $result['visualization_properties']['max_graph_elements'] = 6; return $result; } diff --git a/core/Visualization/JqplotGraph/Evolution.php b/core/Visualization/JqplotGraph/Evolution.php index 5d7734061b..f385d006cb 100644 --- a/core/Visualization/JqplotGraph/Evolution.php +++ b/core/Visualization/JqplotGraph/Evolution.php @@ -25,13 +25,18 @@ class Evolution extends JqplotGraph { const GRAPH_HEIGHT = 170; const ID = 'graphEvolution'; + const SERIES_COLOR_COUNT = 8; + + /** + * Controls whether annotations are shown or not. + */ + const HIDE_ANNOTATIONS_VIEW = 'hide_annotations_view'; 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 +46,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'; + $result['visualization_properties']['Evolution']['hide_annotations_view'] = false; return $result; } diff --git a/core/Visualization/JqplotGraph/Pie.php b/core/Visualization/JqplotGraph/Pie.php index b7fbcf4b4d..c944bf060e 100644 --- a/core/Visualization/JqplotGraph/Pie.php +++ b/core/Visualization/JqplotGraph/Pie.php @@ -21,11 +21,11 @@ class Pie extends JqplotGraph { const ID = 'graphPie'; - protected function getDefaultPropertyValues($view) + public static function getDefaultPropertyValues() { - $result = parent::getDefaultPropertyValues($view); - $result['graph_limit'] = 6; - $result['allow_multi_select_series_picker'] = false; + $result = parent::getDefaultPropertyValues(); + $result['visualization_properties']['max_graph_elements'] = 6; + $result['visualization_properties']['allow_multi_select_series_picker'] = false; return $result; } |