From cdc09042e2826759b3e7db8c1f62cd9678d34230 Mon Sep 17 00:00:00 2001 From: YuCheng Hu Date: Sat, 10 Jul 2010 01:18:47 +0000 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20Piwik=20=E5=88=B0=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=BA=93=E4=B8=AD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +YUCHENG HU+ git-svn-id: https://svn.code.sf.net/p/hawebs/svn@424 a2543c7e-f6e9-4f8a-8bff-1ffc34733512 --- oss/piwik/trunk/core/ViewDataTable/Cloud.php | 124 ++++++++++ .../core/ViewDataTable/GenerateGraphData.php | 155 +++++++++++++ .../GenerateGraphData/ChartEvolution.php | 216 ++++++++++++++++++ .../GenerateGraphData/ChartPie.php | 32 +++ .../GenerateGraphData/ChartVerticalBar.php | 32 +++ .../core/ViewDataTable/GenerateGraphHTML.php | 169 ++++++++++++++ .../GenerateGraphHTML/ChartEvolution.php | 83 +++++++ .../GenerateGraphHTML/ChartPie.php | 30 +++ .../GenerateGraphHTML/ChartVerticalBar.php | 31 +++ .../trunk/core/ViewDataTable/HtmlTable.php | 213 +++++++++++++++++ .../ViewDataTable/HtmlTable/AllColumns.php | 54 +++++ .../core/ViewDataTable/HtmlTable/Goals.php | 157 +++++++++++++ .../trunk/core/ViewDataTable/Sparkline.php | 105 +++++++++ oss/piwik/trunk/core/Visualization/Chart.php | 200 ++++++++++++++++ .../core/Visualization/Chart/Evolution.php | 92 ++++++++ .../trunk/core/Visualization/Chart/Pie.php | 73 ++++++ .../core/Visualization/Chart/VerticalBar.php | 76 ++++++ oss/piwik/trunk/core/Visualization/Cloud.php | 108 +++++++++ .../trunk/core/Visualization/Sparkline.php | 99 ++++++++ 19 files changed, 2049 insertions(+) create mode 100644 oss/piwik/trunk/core/ViewDataTable/Cloud.php create mode 100644 oss/piwik/trunk/core/ViewDataTable/GenerateGraphData.php create mode 100644 oss/piwik/trunk/core/ViewDataTable/GenerateGraphData/ChartEvolution.php create mode 100644 oss/piwik/trunk/core/ViewDataTable/GenerateGraphData/ChartPie.php create mode 100644 oss/piwik/trunk/core/ViewDataTable/GenerateGraphData/ChartVerticalBar.php create mode 100644 oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML.php create mode 100644 oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php create mode 100644 oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML/ChartPie.php create mode 100644 oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML/ChartVerticalBar.php create mode 100644 oss/piwik/trunk/core/ViewDataTable/HtmlTable.php create mode 100644 oss/piwik/trunk/core/ViewDataTable/HtmlTable/AllColumns.php create mode 100644 oss/piwik/trunk/core/ViewDataTable/HtmlTable/Goals.php create mode 100644 oss/piwik/trunk/core/ViewDataTable/Sparkline.php create mode 100644 oss/piwik/trunk/core/Visualization/Chart.php create mode 100644 oss/piwik/trunk/core/Visualization/Chart/Evolution.php create mode 100644 oss/piwik/trunk/core/Visualization/Chart/Pie.php create mode 100644 oss/piwik/trunk/core/Visualization/Chart/VerticalBar.php create mode 100644 oss/piwik/trunk/core/Visualization/Cloud.php create mode 100644 oss/piwik/trunk/core/Visualization/Sparkline.php diff --git a/oss/piwik/trunk/core/ViewDataTable/Cloud.php b/oss/piwik/trunk/core/ViewDataTable/Cloud.php new file mode 100644 index 00000000..735f9f5f --- /dev/null +++ b/oss/piwik/trunk/core/ViewDataTable/Cloud.php @@ -0,0 +1,124 @@ +displayLogoInsteadOfLabel = $bool; + } + + protected function getViewDataTableId() + { + return 'cloud'; + } + + /** + * @see Piwik_ViewDataTable::init() + */ + function init($currentControllerName, + $currentControllerAction, + $apiMethodToRequestDataTable, + $controllerActionCalledWhenRequestSubTable = null) + { + parent::init($currentControllerName, + $currentControllerAction, + $apiMethodToRequestDataTable, + $controllerActionCalledWhenRequestSubTable); + $this->dataTableTemplate = 'CoreHome/templates/cloud.tpl'; + $this->disableOffsetInformation(); + $this->disableExcludeLowPopulation(); + } + + /** + * @see Piwik_ViewDataTable::main() + * + */ + public function main() + { + $this->setLimit( 30 ); + if($this->mainAlreadyExecuted) + { + return; + } + $this->mainAlreadyExecuted = true; + + $this->isDataAvailable = true; + try { + $this->loadDataTableFromAPI(); + } catch(Exception $e) { + $this->isDataAvailable = false; + } + $this->view = $this->buildView(); + } + + function getColumnToDisplay() + { + $columns = parent::getColumnsToDisplay(); + // not label, but the first numeric column + return $columns[1]; + } + + protected function buildView() + { + $view = new Piwik_View($this->dataTableTemplate); + if(!$this->isDataAvailable) + { + $view->cloudValues = array(); + } + else + { + $columnToDisplay = $this->getColumnToDisplay(); + $columnTranslation = $this->getColumnTranslation($columnToDisplay); + $values = $this->dataTable->getColumn($columnToDisplay); + $labels = $this->dataTable->getColumn('label'); + $labelMetadata = array(); + foreach($this->dataTable->getRows() as $row) + { + $logo = false; + if($this->displayLogoInsteadOfLabel) + { + $logo = $row->getMetadata('logo'); + } + $labelMetadata[$row->getColumn('label')] = array( + 'logo' => $logo, + 'url' => $row->getMetadata('url'), + ); + } + $cloud = new Piwik_Visualization_Cloud(); + foreach($labels as $i => $label) + { + $cloud->addWord($label, $values[$i]); + } + $cloudValues = $cloud->render('array'); + foreach($cloudValues as &$value) + { + $value['logoWidth'] = round(max(16, $value['percent'])); + } + $view->columnTranslation = $columnTranslation; + $view->labelMetadata = $labelMetadata; + $view->cloudValues = $cloudValues; + } + $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet(); + $view->properties = $this->getViewProperties(); + return $view; + } +} diff --git a/oss/piwik/trunk/core/ViewDataTable/GenerateGraphData.php b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphData.php new file mode 100644 index 00000000..90e3366a --- /dev/null +++ b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphData.php @@ -0,0 +1,155 @@ + + * function getWebsites( $fetch = false) + * { + * $view = Piwik_ViewDataTable::factory(); + * $view->init( $this->pluginName, 'getWebsites', 'Referers.getWebsites', 'getUrlsFromWebsiteId' ); + * $view->setColumnsToDisplay( array('label','nb_visits') ); + * $view->setLimit(10); + * $view->setGraphLimit(12); + * return $this->renderView($view, $fetch); + * } + * + * + * @package Piwik + * @subpackage Piwik_ViewDataTable + */ +abstract class Piwik_ViewDataTable_GenerateGraphData extends Piwik_ViewDataTable +{ + /** + * Number of elements to display in the graph. + * @var int + */ + protected $graphLimit = null; + protected $yAxisUnit = ''; + + public function setAxisYUnit($unit) + { + $this->yAxisUnit = $unit; + } + + /** + * Sets the number max of elements to display (number of pie slice, vertical bars, etc.) + * If the data has more elements than $limit then the last part of the data will be the sum of all the remaining data. + * + * @param int $limit + */ + public function setGraphLimit( $limit ) + { + $this->graphLimit = $limit; + } + + /** + * Returns numbers of elemnts to display in the graph + * + * @return int + */ + public function getGraphLimit() + { + return $this->graphLimit; + } + + protected $displayPercentageInTooltip = true; + + /** + * The percentage in tooltips is computed based on the sum of all values for the plotted column. + * If the sum of the column in the data set is not the number of elements in the data set, + * for example when plotting visits that have a given plugin enabled: + * one visit can have several plugins, hence the sum is much greater than the number of visits. + * In this case displaying the percentage doesn't make sense. + */ + public function disallowPercentageInGraphTooltip() + { + $this->displayPercentageInTooltip = false; + } + + public function main() + { + if($this->mainAlreadyExecuted) + { + return; + } + $this->mainAlreadyExecuted = true; + + if (!Zend_Registry::get('config')->General->serve_widget_and_data) + { + @header( "Content-Type: application/json" ); + } + + // Graphs require the full dataset, setting limit to null (same as 'no limit') + $this->setLimit(null); + + // 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->disableQueuedFilters(); + + // throws exception if no view access + $this->loadDataTableFromAPI(); + + $graphLimit = $this->getGraphLimit(); + if(!empty($graphLimit)) + { + $offsetStartSummary = $this->getGraphLimit() - 1; + $this->dataTable->filter('AddSummaryRow', + array($offsetStartSummary, + Piwik_Translate('General_Others'), + Piwik_Archive::INDEX_NB_VISITS + ) + ); + } + $this->isDataAvailable = $this->dataTable->getRowsCount() != 0; + + if(!$this->isDataAvailable) + { + $this->view->setTitle(html_entity_decode(Piwik_Translate('General_NoDataForGraph'), ENT_COMPAT, 'UTF-8'), '{font-size: 25px;}'); + } + else + { + $this->initChartObjectData(); + } + $this->view->customizeChartProperties(); + } + + protected function initChartObjectData() + { + $this->dataTable->applyQueuedFilters(); + + // We apply a filter to the DataTable, decoding the label column (useful for keywords for example) + $this->dataTable->filter('ColumnCallbackReplace', array('label','urldecode')); + + $xLabels = $this->dataTable->getColumn('label'); + $columnNames = parent::getColumnsToDisplay(); + if(($labelColumnFound = array_search('label',$columnNames)) !== false) + { + unset($columnNames[$labelColumnFound]); + } + + $columnNameToTranslation = $columnNameToValue = array(); + foreach($columnNames as $columnName) + { + $columnNameToTranslation[$columnName] = $this->getColumnTranslation($columnName); + $columnNameToValue[$columnName] = $this->dataTable->getColumn($columnName); + } + $this->view->setAxisXLabels($xLabels); + $this->view->setAxisYValues($columnNameToValue); + $this->view->setAxisYLabels($columnNameToTranslation); + $this->view->setAxisYUnit($this->yAxisUnit); + $this->view->setDisplayPercentageInTooltip($this->displayPercentageInTooltip); + } +} diff --git a/oss/piwik/trunk/core/ViewDataTable/GenerateGraphData/ChartEvolution.php b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphData/ChartEvolution.php new file mode 100644 index 00000000..a4d774bf --- /dev/null +++ b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphData/ChartEvolution.php @@ -0,0 +1,216 @@ +view = new Piwik_Visualization_Chart_Evolution(); + } + + protected function guessUnitFromRequestedColumnNames($requestedColumnNames, $idSite) + { + $nameToUnit = array( + '_rate' => '%', + '_revenue' => Piwik::getCurrency($idSite), + ); + foreach($requestedColumnNames as $columnName) + { + foreach($nameToUnit as $pattern => $type) + { + if(strpos($columnName, $pattern) !== false) + { + return $type; + } + } + } + return false; + } + + protected function initChartObjectData() + { + // if the loaded datatable is a simple DataTable, it is most likely a plugin plotting some custom data + // we don't expect plugin developers to return a well defined Piwik_DataTable_Array + if($this->dataTable instanceof Piwik_DataTable) + { + return parent::initChartObjectData(); + } + + $this->dataTable->applyQueuedFilters(); + if(!($this->dataTable instanceof Piwik_DataTable_Array)) + { + throw new Exception("Expecting a DataTable_Array with custom format to draw an evolution chart"); + } + + // the X label is extracted from the 'period' object in the table's metadata + $xLabels = $uniqueIdsDataTable = array(); + foreach($this->dataTable->metadata as $idDataTable => $metadataDataTable) + { + //eg. "Aug 2009" + $xLabels[] = html_entity_decode($metadataDataTable['period']->getLocalizedShortString(), ENT_COMPAT, 'UTF-8'); + // we keep track of all unique data table that we need to set a Y value for + $uniqueIdsDataTable[] = $idDataTable; + } + + $requestedColumnNames = $this->getColumnsToDisplay(); + $yAxisLabelToValue = array(); + foreach($this->dataTable->getArray() as $idDataTable => $dataTable) + { + foreach($dataTable->getRows() as $row) + { + $rowLabel = $row->getColumn('label'); + foreach($requestedColumnNames as $requestedColumnName) + { + $metricLabel = $this->getColumnTranslation($requestedColumnName); + if($rowLabel !== false) + { + // eg. "Yahoo! (Visits)" + $yAxisLabel = "$rowLabel ($metricLabel)"; + } + else + { + // eg. "Visits" + $yAxisLabel = $metricLabel; + } + if(($columnValue = $row->getColumn($requestedColumnName)) !== false) + { + $yAxisLabelToValue[$yAxisLabel][$idDataTable] = $columnValue; + } + } + } + } + + // make sure all column values are set to at least zero (no gap in the graph) + $yAxisLabelToValueCleaned = array(); + $yAxisLabels = array(); + foreach($uniqueIdsDataTable as $uniqueIdDataTable) + { + foreach($yAxisLabelToValue as $yAxisLabel => $idDataTableToColumnValue) + { + $yAxisLabels[$yAxisLabel] = $yAxisLabel; + if(isset($idDataTableToColumnValue[$uniqueIdDataTable])) + { + $columnValue = $idDataTableToColumnValue[$uniqueIdDataTable]; + } + else + { + $columnValue = 0; + } + $yAxisLabelToValueCleaned[$yAxisLabel][] = $columnValue; + } + } + $idSite = Piwik_Common::getRequestVar('idSite'); + + $unit = $this->yAxisUnit; + if(empty($unit)) + { + $unit = $this->guessUnitFromRequestedColumnNames($requestedColumnNames, $idSite); + } + + $this->view->setAxisXLabels($xLabels); + $this->view->setAxisYValues($yAxisLabelToValueCleaned); + $this->view->setAxisYLabels($yAxisLabels); + $this->view->setAxisYUnit($unit); + + $firstDatatable = reset($this->dataTable->metadata); + $period = $firstDatatable['period']; + switch($period->getLabel()) { + case 'day': $steps = 7; break; + case 'week': $steps = 10; break; + case 'month': $steps = 6; break; + case 'year': $steps = 2; break; + default: $steps = 10; break; + } + $this->view->setXSteps($steps); + + if($this->isLinkEnabled()) + { + $axisXOnClick = array(); + $queryStringAsHash = $this->getQueryStringAsHash(); + foreach($this->dataTable->metadata as $idDataTable => $metadataDataTable) + { + $period = $metadataDataTable['period']; + $dateInUrl = $period->getDateStart(); + $parameters = array( + 'idSite' => $idSite, + 'period' => $period->getLabel(), + 'date' => $dateInUrl->toString() + ); + $hash = ''; + if(!empty($queryStringAsHash)) + { + $hash = '#' . Piwik_Url::getQueryStringFromParameters( $queryStringAsHash + $parameters); + } + $link = Piwik_Url::getCurrentUrlWithoutQueryString() . + '?' . + Piwik_Url::getQueryStringFromParameters( array( + 'module' => 'CoreHome', + 'action' => 'index', + ) + $parameters) + . $hash; + $axisXOnClick[] = $link; + } + $this->view->setAxisXOnClick($axisXOnClick); + } + } + + /** + * We link the graph dots to the same report as currently being displayed (only the date would change). + * + * In some cases the widget is loaded within a report that doesn't exist as such. + * For example, the dashboards loads the 'Last visits graph' widget which can't be directly linked to. + * Instead, the graph must link back to the dashboard. + * + * In other cases, like Visitors>Overview or the Goals graphs, we can link the graph clicks to the same report. + * + * To detect whether or not we can link to a report, we simply check if the current URL from which it was loaded + * belongs to the menu or not. If it doesn't belong to the menu, we do not append the hash to the URL, + * which results in loading the dashboard. + * + * @return array Query string array to append to the URL hash or false if the dashboard should be displayed + */ + private function getQueryStringAsHash() + { + $queryString = Piwik_Url::getArrayFromCurrentQueryString(); + $piwikParameters = array('idSite', 'date', 'period', 'XDEBUG_SESSION_START', 'KEY'); + foreach($piwikParameters as $parameter) + { + unset($queryString[$parameter]); + } + if(Piwik_IsMenuUrlFound($queryString)) + { + return $queryString; + } + return false; + } + + private function isLinkEnabled() + { + static $linkEnabled; + if(!isset($linkEnabled)) + { + $linkEnabled = !Piwik_Common::getRequestVar('disableLink', 0, 'int'); + } + return $linkEnabled; + } +} diff --git a/oss/piwik/trunk/core/ViewDataTable/GenerateGraphData/ChartPie.php b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphData/ChartPie.php new file mode 100644 index 00000000..1acda43f --- /dev/null +++ b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphData/ChartPie.php @@ -0,0 +1,32 @@ +view = new Piwik_Visualization_Chart_Pie(); + } +} diff --git a/oss/piwik/trunk/core/ViewDataTable/GenerateGraphData/ChartVerticalBar.php b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphData/ChartVerticalBar.php new file mode 100644 index 00000000..b2b6feed --- /dev/null +++ b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphData/ChartVerticalBar.php @@ -0,0 +1,32 @@ +view = new Piwik_Visualization_Chart_VerticalBar(); + } +} diff --git a/oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML.php b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML.php new file mode 100644 index 00000000..1125620a --- /dev/null +++ b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML.php @@ -0,0 +1,169 @@ +dataTableTemplate = 'CoreHome/templates/graph.tpl'; + + $this->disableOffsetInformation(); + $this->disableExcludeLowPopulation(); + $this->disableSearchBox(); + $this->enableShowExportAsImageIcon(); + + $this->parametersToModify = array( + 'viewDataTable' => $this->getViewDataTableIdToLoad(), + // in the case this controller is being executed by another controller + // eg. when being widgetized in an IFRAME + // we need to put in the URL of the graph data the real module and action + 'module' => $currentControllerName, + 'action' => $currentControllerAction, + ); + } + + public function enableShowExportAsImageIcon() + { + $this->viewProperties['show_export_as_image_icon'] = true; + } + + /** + * Sets parameters to modify in the future generated URL + * @param array $array array('nameParameter' => $newValue, ...) + */ + public function setParametersToModify($array) + { + $this->parametersToModify = array_merge($this->parametersToModify, $array); + } + + /** + * We persist the parametersToModify values in the javascript footer. + * This is used by the "export links" that use the "date" attribute + * from the json properties array in the datatable footer. + */ + protected function getJavascriptVariablesToSet() + { + return $this->parametersToModify + parent::getJavascriptVariablesToSet(); + } + + /** + * @see Piwik_ViewDataTable::main() + */ + public function main() + { + if($this->mainAlreadyExecuted) + { + return; + } + $this->mainAlreadyExecuted = true; + + $this->view = $this->buildView(); + } + + protected function buildView() + { + $view = new Piwik_View($this->dataTableTemplate); + $this->uniqueIdViewDataTable = $this->getUniqueIdViewDataTable(); + $view->graphType = $this->graphType; + $this->chartDivId = $this->uniqueIdViewDataTable . "Chart_swf"; + + $this->parametersToModify['action'] = $this->currentControllerAction; + $this->parametersToModify = array_merge($this->variablesDefault, $this->parametersToModify); + + $url = Piwik_Url::getCurrentQueryStringWithParametersModified($this->parametersToModify); + + $this->includeData = Zend_Registry::get('config')->General->serve_widget_and_data; + $idSite = Piwik_Common::getRequestVar('idSite', 1); + if(Piwik::isUserHasViewAccess($idSite) && $this->includeData) + { + $this->chartData = $this->getFlashData(); + } + else + { + $this->chartData = null; + } + $view->flashParameters = $this->getFlashParameters(); + $view->urlGraphData = $url; + $view->chartDivId = $this->chartDivId; + $view->formEmbedId = "formEmbed".$this->uniqueIdViewDataTable; + $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet(); + $view->properties = $this->getViewProperties(); + return $view; + } + + protected function getFlashData() + { + $saveGet = $_GET; + + foreach($this->parametersToModify as $key => $val) + { + // We do not forward filter data to the graph controller. + // This would cause the graph to have filter_limit=5 set by default, + // which would break them (graphs need the full dataset to build the "Others" aggregate value) + if(strpos($key, 'filter_') !== false) + { + continue; + } + if (is_array($val)) + { + $_GET[$key] = unserialize(serialize($val)); + } + else + { + $_GET[$key] = $val; + } + } + $content = Piwik_FrontController::getInstance()->fetchDispatch( $this->currentControllerName, $this->currentControllerAction, array()); + + $_GET = $saveGet; + + return str_replace(array("\r", "\n", "'", '\"'), array('', '', "\\'", '\\\"'), $content); + } + + protected function getFlashParameters() + { + // chart title is only set when there's no data in the graph + $isDataAvailable = $this->chartData && !preg_match('/],\s+"title": {/', $this->chartData); + + return array( + 'width' => $this->width, + 'height' => $this->height, + 'ofcLibraryPath' => 'libs/open-flash-chart/', + 'swfLibraryPath' => 'libs/swfobject/', + 'requiredFlashVersion' => '9.0.0', + 'isDataAvailable' => $isDataAvailable, + 'includeData' => $this->includeData, + 'data' => $this->chartData, + ); + } +} diff --git a/oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php new file mode 100644 index 00000000..209c16e1 --- /dev/null +++ b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php @@ -0,0 +1,83 @@ +width='100%'; + $this->height=150; + // used for the CSS class to apply to the DIV containing the graph + $this->graphType = 'evolution'; + } + + protected function getViewDataTableId() + { + return 'graphEvolution'; + } + + protected function getViewDataTableIdToLoad() + { + return 'generateDataChartEvolution'; + } + + function init($currentControllerName, + $currentControllerAction, + $apiMethodToRequestDataTable, + $controllerActionCalledWhenRequestSubTable = null) + { + parent::init($currentControllerName, + $currentControllerAction, + $apiMethodToRequestDataTable, + $controllerActionCalledWhenRequestSubTable); + + $this->setParametersToModify(array('date' => Piwik_Common::getRequestVar('date', 'last30', 'string'))); + $this->disableShowAllViewsIcons(); + $this->disableShowTable(); + } + + + /** + * We ensure that the graph for a given Goal has a different ID than the 'Goals Overview' graph + * so that both can display on the dashboard at the same time + */ + public function getUniqueIdViewDataTable() + { + $id = parent::getUniqueIdViewDataTable(); + if(isset($this->parametersToModify['idGoal'])) + { + $id .= $this->parametersToModify['idGoal']; + } + return $id; + } + + /** + * Sets the columns that will be displayed on output evolution chart + * By default all columns are displayed ($columnsNames = array() will display all columns) + * + * @param array $columnsNames Array of column names eg. array('nb_visits','nb_hits') + */ + public function setColumnsToDisplay( $columnsNames) + { + if(!is_array($columnsNames)) + { + $columnsNames = array($columnsNames); + } + $this->setParametersToModify( array('columns' => $columnsNames) ); + } +} diff --git a/oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML/ChartPie.php b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML/ChartPie.php new file mode 100644 index 00000000..692bdf4f --- /dev/null +++ b/oss/piwik/trunk/core/ViewDataTable/GenerateGraphHTML/ChartPie.php @@ -0,0 +1,30 @@ +dataTableTemplate = 'CoreHome/templates/datatable.tpl'; + $this->variablesDefault['enable_sort'] = '1'; + $this->setSortedColumn('nb_visits', 'desc'); + $this->setLimit(Zend_Registry::get('config')->General->datatable_default_limit); + $this->handleLowPopulation(); + } + + protected function getViewDataTableId() + { + return 'table'; + } + + /** + * @see Piwik_ViewDataTable::main() + */ + public function main() + { + if($this->mainAlreadyExecuted) + { + return; + } + $this->mainAlreadyExecuted = true; + + $this->isDataAvailable = true; + try { + $this->loadDataTableFromAPI(); + } catch(Exception $e) { + $this->isDataAvailable = false; + } + + $this->postDataTableLoadedFromAPI(); + $this->view = $this->buildView(); + } + + /** + * @return Piwik_View with all data set + */ + protected function buildView() + { + $view = new Piwik_View($this->dataTableTemplate); + + if(!$this->isDataAvailable) + { + $view->arrayDataTable = array(); + } + else + { + $columns = $this->getColumnsToDisplay(); + $columnTranslations = $columnDescriptions = array(); + foreach($columns as $columnName) + { + $columnTranslations[$columnName] = $this->getColumnTranslation($columnName); + $columnDescriptions[$columnName] = $this->getColumnDescription($columnName); + } + $nbColumns = count($columns); + // case no data in the array we use the number of columns set to be displayed + if($nbColumns == 0) + { + $nbColumns = count($this->columnsToDisplay); + } + + $view->arrayDataTable = $this->getPHPArrayFromDataTable(); + $view->dataTableColumns = $columns; + $view->columnTranslations = $columnTranslations; + $view->columnDescriptions = $columnDescriptions; + $view->nbColumns = $nbColumns; + $view->defaultWhenColumnValueNotDefined = '-'; + } + $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet(); + $view->properties = $this->getViewProperties(); + return $view; + } + + protected function handleLowPopulation( $columnToApplyFilter = null) + { + if(Piwik_Common::getRequestVar('enable_filter_excludelowpop', '0', 'string' ) == '0') + { + return; + } + if(is_null($columnToApplyFilter)) + { + $columnToApplyFilter = Piwik_Archive::INDEX_NB_VISITS; + } + $this->setExcludeLowPopulation( $columnToApplyFilter); + } + + /** + * Returns friendly php array from the Piwik_DataTable + * @see Piwik_DataTable_Renderer_Php + * @return array + */ + protected function getPHPArrayFromDataTable() + { + $renderer = Piwik_DataTable_Renderer::factory('php'); + $renderer->setTable($this->dataTable); + $renderer->setSerialize( false ); + // we get the php array from the datatable but conserving the original datatable format, + // ie. rows 'columns', 'metadata' and 'idsubdatatable' + $phpArray = $renderer->originalRender(); + return $phpArray; + } + + + /** + * Adds a column to the list of columns to be displayed + * + * @param string $columnName + */ + public function addColumnToDisplay( $columnName ) + { + $this->columnsToDisplay[] = $columnName; + } + + /** + */ + public function disableSort() + { + $this->variablesDefault['enable_sort'] = 'false'; + } + + /** + * Sets the search on a table to be recursive (also searches in subtables) + * Works only on Actions/Downloads/Outlinks tables. + * + * @return bool If the pattern for a recursive search was set in the URL + */ + public function setSearchRecursive() + { + $this->variablesDefault['search_recursive'] = true; + return $this->setRecursiveLoadDataTableIfSearchingForPattern(); + } + + protected function getRequestString() + { + $requestString = parent::getRequestString(); + if($this->recursiveDataTableLoad) + { + $requestString .= '&expanded=1'; + } + return $requestString; + } + + /** + * Set the flag to load the datatable recursively so we can search on subtables as well + * + * @return bool if recursive search is enabled + */ + protected function setRecursiveLoadDataTableIfSearchingForPattern() + { + try{ + $requestValue = Piwik_Common::getRequestVar('filter_column_recursive'); + $requestValue = Piwik_Common::getRequestVar('filter_pattern_recursive'); + // if the 2 variables are set we are searching for something. + // we have to load all the children subtables in this case + + $this->recursiveDataTableLoad = true; + return true; + } + catch(Exception $e) { + $this->recursiveDataTableLoad = false; + return false; + } + } +} diff --git a/oss/piwik/trunk/core/ViewDataTable/HtmlTable/AllColumns.php b/oss/piwik/trunk/core/ViewDataTable/HtmlTable/AllColumns.php new file mode 100644 index 00000000..c612025f --- /dev/null +++ b/oss/piwik/trunk/core/ViewDataTable/HtmlTable/AllColumns.php @@ -0,0 +1,54 @@ +viewProperties['show_exclude_low_population'] = true; + parent::main(); + } + + protected function getRequestString() + { + $requestString = parent::getRequestString(); + return $requestString . '&filter_add_columns_when_show_all_columns=1'; + } + + protected function postDataTableLoadedFromAPI() + { + parent::postDataTableLoadedFromAPI(); + Piwik_Controller::setPeriodVariablesView($this); + $columnUniqueVisitors = false; + if($this->period == 'day') + { + $columnUniqueVisitors = 'nb_uniq_visitors'; + } + $this->setColumnsToDisplay(array('label', + 'nb_visits', + $columnUniqueVisitors, + 'nb_actions_per_visit', + 'avg_time_on_site', + 'bounce_rate')); + $this->dataTable->filter('ColumnCallbackReplace', array('avg_time_on_site', create_function('$averageTimeOnSite', 'return Piwik::getPrettyTimeFromSeconds($averageTimeOnSite);'))); + $this->dataTable->filter('ColumnCallbackReplace', array('bounce_rate', create_function('$bounceRate', 'return $bounceRate."%";'))); + } +} diff --git a/oss/piwik/trunk/core/ViewDataTable/HtmlTable/Goals.php b/oss/piwik/trunk/core/ViewDataTable/HtmlTable/Goals.php new file mode 100644 index 00000000..dfa2f581 --- /dev/null +++ b/oss/piwik/trunk/core/ViewDataTable/HtmlTable/Goals.php @@ -0,0 +1,157 @@ +idSite = Piwik_Common::getRequestVar('idSite', null, 'int'); + $this->processOnlyIdGoal = Piwik_Common::getRequestVar('filter_only_display_idgoal', 0, 'int'); + $this->viewProperties['show_exclude_low_population'] = true; + $this->viewProperties['show_goals'] = true; + + $this->setColumnsTranslations( array( + 'goal_%s_conversion_rate' => '%s conversion rate', + 'goal_%s_nb_conversions' => '%s conversions', + 'goal_%s_revenue_per_visit' => '%s revenue per visit', + )); + + $this->setColumnsToDisplay( array( + 'label', + 'nb_visits', + 'goal_%s_nb_conversions', + 'goal_%s_conversion_rate', + 'goal_%s_revenue_per_visit', + 'goals_conversion_rate', + 'revenue_per_visit', + )); + + // We ensure that the 'Sort by' column is actually displayed in the table + // eg. most daily reports sort by nb_uniq_visitors but this column is not displayed in the Goals table + $columnsToDisplay = $this->getColumnsToDisplay(); + $columnToSortBy = $this->getSortedColumn(); + if(!in_array($columnToSortBy, $columnsToDisplay)) + { + $this->setSortedColumn('nb_visits', 'desc'); + } + parent::main(); + } + + public function disableSubTableWhenShowGoals() + { + $this->controllerActionCalledWhenRequestSubTable = null; + } + + public function setColumnsToDisplay($columnsNames) + { + $newColumnsNames = array(); + $goals = array(); + $idSite = $this->getIdSite(); + if($idSite) + { + $goals = Piwik_Goals_API::getInstance()->getGoals( $idSite ); + } + foreach($columnsNames as $columnName) + { + if(in_array($columnName, array('goal_%s_conversion_rate', 'goal_%s_nb_conversions', 'goal_%s_revenue_per_visit'))) + { + foreach($goals as $goal) + { + $idgoal = $goal['idgoal']; + if($this->processOnlyIdGoal > Piwik_DataTable_Filter_UpdateColumnsWhenShowAllGoals::GOALS_FULL_TABLE + && $this->processOnlyIdGoal != $idgoal) + { + continue; + } + $name = Piwik_Translate($this->getColumnTranslation($columnName), $goal['name']); + $columnNameGoal = str_replace('%s', $idgoal, $columnName); + $this->setColumnTranslation($columnNameGoal, $name); + if(strstr($columnNameGoal, '_rate') !== false) + { + $this->columnsToPercentageFilter[] = $columnNameGoal; + } + // For the goal table (when the flag icon is clicked), we only display the per Goal Conversion rate + elseif($this->processOnlyIdGoal == Piwik_DataTable_Filter_UpdateColumnsWhenShowAllGoals::GOALS_OVERVIEW) + { + continue; + } + + if(strstr($columnNameGoal, '_revenue') !== false) + { + $this->columnsToRevenueFilter[] = $columnNameGoal; + } + else + { + $this->columnsToConversionFilter[] = $columnNameGoal; + } + $newColumnsNames[] = $columnNameGoal; + } + } + else + { + $newColumnsNames[] = $columnName; + } + } + parent::setColumnsToDisplay($newColumnsNames); + } + + protected function getRequestString() + { + $requestString = parent::getRequestString(); + if($this->processOnlyIdGoal > Piwik_DataTable_Filter_UpdateColumnsWhenShowAllGoals::GOALS_FULL_TABLE) + { + $requestString .= "&filter_only_display_idgoal=".$this->processOnlyIdGoal; + } + return $requestString . '&filter_update_columns_when_show_all_goals=1'; + } + + protected $columnsToPercentageFilter = array(); + protected $columnsToRevenueFilter = array(); + protected $columnsToConversionFilter = array(); + protected $idSite = false; + + private function getIdSite() + { + return $this->idSite; + } + + protected function postDataTableLoadedFromAPI() + { + parent::postDataTableLoadedFromAPI(); + $this->columnsToPercentageFilter[] = 'goals_conversion_rate'; + foreach($this->columnsToPercentageFilter as $columnName) + { + $this->dataTable->filter('ColumnCallbackReplace', array($columnName, create_function('$rate', 'return sprintf("%.1f",$rate)."%";'))); + } + $this->columnsToRevenueFilter[] = 'revenue_per_visit'; + foreach($this->columnsToRevenueFilter as $columnName) + { + $this->dataTable->filter('ColumnCallbackReplace', array($columnName, create_function('$value', 'return sprintf("%.1f",$value);'))); + $this->dataTable->filter('ColumnCallbackReplace', array($columnName, array("Piwik", "getPrettyMoney"), array($this->getIdSite()))); + } + + foreach($this->columnsToConversionFilter as $columnName) + { + // this ensures that the value is set to zero for all rows where the value was not set (no conversion) + $this->dataTable->filter('ColumnCallbackReplace', array($columnName, create_function('$value', 'return $value;'))); + } + } +} diff --git a/oss/piwik/trunk/core/ViewDataTable/Sparkline.php b/oss/piwik/trunk/core/ViewDataTable/Sparkline.php new file mode 100644 index 00000000..071ed2bb --- /dev/null +++ b/oss/piwik/trunk/core/ViewDataTable/Sparkline.php @@ -0,0 +1,105 @@ +mainAlreadyExecuted) + { + return; + } + $this->mainAlreadyExecuted = true; + + // throws exception if no view access + $this->loadDataTableFromAPI(); + + $this->isDataAvailable = $this->dataTable->getRowsCount() != 0; + if(!$this->isDataAvailable) + { + throw new Exception(Piwik_TranslateException('General_NoDataForGraph')); + } + $values = $this->getValuesFromDataTable($this->dataTable); + $graph = new Piwik_Visualization_Sparkline(); + $graph->setValues($values); + $graph->main(); + + $this->view = $graph; + } + + protected function getValuesFromDataTableArray( $dataTableArray, $columnToPlot ) + { + $dataTableArray->applyQueuedFilters(); + $values = array(); + foreach($dataTableArray->getArray() as $table) + { + if($table->getRowsCount() > 1) + { + throw new Exception("Expecting only one row per DataTable"); + } + $value = 0; + $onlyRow = $table->getFirstRow(); + if($onlyRow !== false) + { + if(!empty($columnToPlot)) + { + $value = $onlyRow->getColumn($columnToPlot); + } + // if not specified, we load by default the first column found + // eg. case of getLastDistinctCountriesGraph + else + { + $columns = $onlyRow->getColumns(); + $value = current($columns); + } + } + $values[] = $value; + } + return $values; + } + + protected function getValuesFromDataTable( $dataTable ) + { + $columns = $this->getColumnsToDisplay(); + $columnToPlot = false; + if(!empty($columns)) + { + $columnToPlot = $columns[0]; + } + + // a Piwik_DataTable_Array is returned when using the normal code path to request data from Archives, in all core plugins + // however plugins can also return simple datatable, hence why the sparkline can accept both data types + if($this->dataTable instanceof Piwik_DataTable_Array) + { + $values = $this->getValuesFromDataTableArray($dataTable, $columnToPlot); + } + elseif($this->dataTable instanceof Piwik_DataTable) + { + $values = $this->dataTable->getColumn($columnToPlot); + } + return $values; + } +} diff --git a/oss/piwik/trunk/core/Visualization/Chart.php b/oss/piwik/trunk/core/Visualization/Chart.php new file mode 100644 index 00000000..dc3dff49 --- /dev/null +++ b/oss/piwik/trunk/core/Visualization/Chart.php @@ -0,0 +1,200 @@ +chart = new open_flash_chart(); + } + + public function setAxisXLabels($xLabels) + { + $this->xLabels = $xLabels; + } + + public function setAxisXOnClick($onClick) + { + $this->xOnClick = $onClick; + } + + public function setAxisYValues($values) + { + $this->yValues = $values; + } + + function setAxisYUnit($yUnit) + { + if(!empty($yUnit)) + { + $this->yUnit = $yUnit; + } + } + + public function setAxisYLabels($labels) + { + $this->yLabels = $labels; + } + + public function setDisplayPercentageInTooltip($bool) + { + $this->displayPercentageInTooltip = $bool; + } + + public function setXSteps($steps) + { + $this->xSteps = $steps; + } + + protected function getDataSetsToDisplay() + { + if(empty($this->yValues)) { + return false; + } + return array_keys($this->yValues); + } + + public function getMaxValue() + { + $datasetsIds = $this->getDataSetsToDisplay(); + if($datasetsIds === false) + { + return 0; + } + $maxCrossDataSets = false; + foreach($datasetsIds as $dataset) + { + $maxValue = max($this->yValues[$dataset]); + if($maxCrossDataSets === false + || $maxValue > $maxCrossDataSets) + { + $maxCrossDataSets = $maxValue; + } + } + if($maxCrossDataSets > 10) + { + $maxCrossDataSets = $maxCrossDataSets + 10 - $maxCrossDataSets % 10; + } + return $maxCrossDataSets; + } + + public function setTitle($text, $css) + { + $title = new title($text); + $title->set_style($css); + $this->chart->set_title($title); + } + + public function render() + { + if(Piwik_Url::getCurrentScheme() == 'https' || + Zend_Registry::get('config')->General->reverse_proxy) + { + @header("Pragma: "); + @header("Cache-Control: must-revalidate"); + } + return $this->chart->toPrettyString(); + } + + function customizeChartProperties() + { + $this->chart->set_number_format($num_decimals = 0, + $is_fixed_num_decimals_forced = true, + $is_decimal_separator_comma = false, + $is_thousand_separator_disabled = false); + + $gridColour = '#E0E1E4'; + $countValues = count($this->xLabels); + $this->maxValue = $this->getMaxValue(); + $this->minValue = 0; + + // X Axis + $this->x = new x_axis(); + $this->x->set_colour( '#596171' ); + $this->x->set_grid_colour( $gridColour ); + $this->x->set_steps($this->xSteps); + + // X Axis Labels + $this->x_labels = new x_axis_labels(); + $this->x_labels->set_size(11); + //manually fix the x labels step as this doesn't work in this OFC release.. + $xLabelsStepped = $this->xLabels; + foreach($xLabelsStepped as $i => &$xLabel) + { + if(($i % $this->xSteps) != 0) + { + $xLabel = ''; + } + } + $this->x_labels->set_labels($xLabelsStepped); + $this->x_labels->set_steps(2); + $this->x->set_labels($this->x_labels); + + // Y Axis + $this->y = new y_axis(); + $this->y->set_colour('#ffffff'); + $this->y->set_grid_colour($gridColour); + $stepsCount = 2; + $stepsEveryNLabel = ceil(($this->maxValue - $this->minValue) / $stepsCount); + if($this->maxValue == 0) + { + $this->maxValue = 1; + } + $this->y->set_range( $this->minValue, (int) ceil($this->maxValue), (int) $stepsEveryNLabel); + $dataSetsToDisplay = $this->getDataSetsToDisplay(); + if($dataSetsToDisplay != false) + { + $dataSetToDisplay = current($dataSetsToDisplay); + $this->y->set_label_text("#val#".$this->yUnit); + } + + // Tooltip + $this->tooltip = new tooltip(); + $this->tooltip->set_shadow( true ); + $this->tooltip->set_stroke( 1 ); + + // Attach elements to the graph + $this->chart->set_x_axis($this->x); + $this->chart->set_y_axis($this->y); + $this->chart->set_tooltip($this->tooltip); + $this->chart->set_bg_colour('#ffffff'); + } +} diff --git a/oss/piwik/trunk/core/Visualization/Chart/Evolution.php b/oss/piwik/trunk/core/Visualization/Chart/Evolution.php new file mode 100644 index 00000000..b4ec58fa --- /dev/null +++ b/oss/piwik/trunk/core/Visualization/Chart/Evolution.php @@ -0,0 +1,92 @@ +getDataSetsToDisplay(); + if($dataSetsToDisplay === false) + { + return; + } + + $colors = array( + "0x3357A0", + "0xCC3399", + "0x9933CC", + "0x80a033", + "0xFD9816", + "0x246AD2", + "0xFD16EA", + "0x49C100", + ); + + $i = 0; + foreach($dataSetsToDisplay as $dataSetToDisplay) + { + $color = $colors[$i % count($colors)]; + + $labelName = $this->yLabels[$dataSetToDisplay]; + $d = new hollow_dot(); + $d->size(3)->halo_size(0)->colour($color); + + $line = new line(); + $line->set_default_dot_style($d); + $line->set_key($labelName, 11); + $line->set_width( 1 ); + $line->set_colour( $color ); + + // Line Values + // Note: we have to manually create the dot values as the steps feature doens't work on X axis + // when it's working again, we can remove code below and set generic tooltip above: // ->tooltip('#x_label#
#val# '.$labelName) + $yValues = $this->yValues[$dataSetToDisplay]; + $labelName = $this->yLabels[$dataSetToDisplay]; + $lineValues = array(); + $j = 0; + foreach($this->xLabels as $label) { + $value = (float)$yValues[$j]; + $lineValue = new hollow_dot($value); + + $unit = $this->yUnit; + $lineValue->tooltip("$label
$value$unit $labelName"); + if(!empty($this->xOnClick)) + { + $lineValue->on_click("piwikHelper.redirectToUrl('".$this->xOnClick[$j]."')"); + } + $lineValues[] = $lineValue; + $j++; + } + $line->set_values( $lineValues ); + $lines[] = $line; + $i++; + } + foreach($lines as $line) + { + $this->chart->add_element($line); + } + // if one column is a percentage we set the grid accordingly + // note: it is invalid to plot a percentage dataset along with a numeric dataset + if($this->yUnit == '%' + && $this->maxValue > 90) + { + $this->y->set_range( 0, 100, 50); + } + } +} diff --git a/oss/piwik/trunk/core/Visualization/Chart/Pie.php b/oss/piwik/trunk/core/Visualization/Chart/Pie.php new file mode 100644 index 00000000..699a6221 --- /dev/null +++ b/oss/piwik/trunk/core/Visualization/Chart/Pie.php @@ -0,0 +1,73 @@ +getDataSetsToDisplay(); + if($dataSetsToDisplay === false) + { + return; + } + $dataSetToDisplay = current($dataSetsToDisplay); + + // create the Pie + $pie = new pie(); + $pie->set_alpha("0.6"); + $pie->set_start_angle( 35 ); + $pie->add_animation( new pie_fade() ); + $pie->set_label_colour('#142448'); + $pie->set_colours( array('#3C5A69','#679BB5','#695A3C','#B58E67','#969696') ); + + // create the Pie values + $yValues = $this->yValues[$dataSetToDisplay]; + $labelName = $this->yLabels[$dataSetToDisplay]; + $unit = $this->yUnit; + $sum = array_sum($yValues); + $pieValues = array(); + $i = 0; + foreach($this->xLabels as $label) { + $value = (float)$yValues[$i]; + $i++; + // we never plot empty pie slices (eg. visits by server time pie chart) + if($value <= 0) { + continue; + } + $pieValue = new pie_value($value, $label); + $percentage = round(100 * $value / $sum); + $pieValue->set_tooltip("$label
$percentage% ($value$unit $labelName)"); + $pieValues[] = $pieValue; + } + $pie->set_values($pieValues); + + $this->chart->add_element($pie); + } +} diff --git a/oss/piwik/trunk/core/Visualization/Chart/VerticalBar.php b/oss/piwik/trunk/core/Visualization/Chart/VerticalBar.php new file mode 100644 index 00000000..61302a02 --- /dev/null +++ b/oss/piwik/trunk/core/Visualization/Chart/VerticalBar.php @@ -0,0 +1,76 @@ +getDataSetsToDisplay(); + if($dataSetsToDisplay === false) + { + return; + } + $dataSetToDisplay = current($dataSetsToDisplay); + + $this->x->set_grid_colour('#ffffff'); + $this->x_labels->set_steps(2); + $this->x->set_stroke(1); + + // create the Bar object + $bar = new bar_filled('#3B5AA9', '#063E7E'); + $bar->set_alpha("0.5"); + $bar->set_key($this->yLabels[$dataSetToDisplay], 12); + $bar->set_tooltip( '#val# #key#'); + + // create the bar values + $yValues = $this->yValues[$dataSetToDisplay]; + $labelName = $this->yLabels[$dataSetToDisplay]; + $unit = $this->yUnit; + $barValues = array(); + $i = 0; + $sum = array_sum($yValues); + foreach($this->xLabels as $label) { + $value = (float)$yValues[$i]; + + $displayPercentage = ''; + if($this->displayPercentageInTooltip) + { + $percentage = round(100 * $value / $sum); + $displayPercentage = "($percentage%)"; + } + $barValue = new bar_value($value); + $barValue->set_tooltip("$label
$value$unit $labelName $displayPercentage"); + $barValues[] = $barValue; + $i++; + } + $bar->set_values($barValues); + $this->chart->add_element($bar); + } +} diff --git a/oss/piwik/trunk/core/Visualization/Cloud.php b/oss/piwik/trunk/core/Visualization/Cloud.php new file mode 100644 index 00000000..9d525b48 --- /dev/null +++ b/oss/piwik/trunk/core/Visualization/Cloud.php @@ -0,0 +1,108 @@ +wordsArray[$word])) + { + $this->wordsArray[$word] += $value; + } + else + { + $this->wordsArray[$word] = $value; + } + } + + public function render() + { + $this->shuffleCloud(); + $return = array(); + if(empty($this->wordsArray)) { + return array(); + } + $maxValue = max($this->wordsArray); + foreach ($this->wordsArray as $word => $popularity) + { + $wordTruncated = $word; + if(strlen($word) > $this->truncatingLimit) + { + $wordTruncated = substr($word, 0, $this->truncatingLimit - 3).'...'; + } + $percent = ($popularity / $maxValue) * 100; + // CSS style value + $sizeRange = $this->getClassFromPercent($percent); + + $return[$word] = array( + 'word' => $word, + 'wordTruncated' => $wordTruncated, + 'value' => $popularity, + 'size' => $sizeRange, + 'percent' => $percent, + ); + } + return $return; + } + + /* + * Shuffle associated names in array + */ + protected function shuffleCloud() + { + $keys = array_keys($this->wordsArray); + + shuffle($keys); + + if (count($keys) && is_array($keys)) + { + $tmpArray = $this->wordsArray; + $this->wordsArray = array(); + foreach ($keys as $key => $value) + $this->wordsArray[$value] = $tmpArray[$value]; + } + } + + /* + * Get the class range using a percentage + * + * @returns int $class + */ + protected function getClassFromPercent($percent) + { + $mapping = array(95, 70, 50, 30, 15, 5, 0); + foreach($mapping as $key => $value) + { + if($percent >= $value) + { + return $key; + } + } + } +} diff --git a/oss/piwik/trunk/core/Visualization/Sparkline.php b/oss/piwik/trunk/core/Visualization/Sparkline.php new file mode 100644 index 00000000..dcc902e2 --- /dev/null +++ b/oss/piwik/trunk/core/Visualization/Sparkline.php @@ -0,0 +1,99 @@ +values = $data; + } + + static public function getWidth() + { + return 100; + } + + static public function getHeight() + { + return 25; + } + + function main() + { + $width = self::getWidth(); + $height = self::getHeight(); + + $sparkline = new Sparkline_Line(); + $sparkline->SetColor('lineColor', 22, 44, 74); // dark blue + $sparkline->SetColorHtml('red', '#FF7F7F'); + $sparkline->SetColorHtml('blue', '#55AAFF'); + $sparkline->SetColorHtml('green', '#75BF7C'); + + $min = $max = $last = null; + $i = 0; + foreach($this->values as $value) + { + // 50% should be plotted as 50 + $toRemove = '%'; + if(strpos($value, $toRemove) !== false) + { + $value = str_replace($toRemove, '', $value); + } + $sparkline->SetData($i, $value); + if( null == $min || $value <= $min[1]) + { + $min = array($i, $value); + } + if(null == $max || $value >= $max[1]) + { + $max = array($i, $value); + } + $last = array($i, $value); + $i++; + } + $sparkline->SetYMin(0); + $sparkline->SetYMax($max[1]); + $sparkline->SetPadding( 3, 0, 2, 0 ); // top, right, bottom, left +// $font = FONT_2; + $sparkline->SetFeaturePoint($min[0], $min[1], 'red', 5); + $sparkline->SetFeaturePoint($max[0], $max[1], 'green', 5); + $sparkline->SetFeaturePoint($last[0], $last[1], 'blue', 5); + $sparkline->SetLineSize(3); // for renderresampled, linesize is on virtual image + $ratio = 1; +// var_dump($min);var_dump($max);var_dump($lasts);exit; + $sparkline->RenderResampled($width*$ratio, $height*$ratio); + + $this->sparkline = $sparkline; + } + + function render() + { + $this->sparkline->Output(); + } +}