PageRenderTime 50ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/plugins/CoreHome/DataTableRowAction/RowEvolution.php

https://github.com/CodeYellowBV/piwik
PHP | 345 lines | 215 code | 59 blank | 71 comment | 23 complexity | b249ad0269afc7208d6d16d6524deae0 MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\Plugins\CoreHome\DataTableRowAction;
  10. use Exception;
  11. use Piwik\API\Request;
  12. use Piwik\API\ResponseBuilder;
  13. use Piwik\Common;
  14. use Piwik\DataTable;
  15. use Piwik\Date;
  16. use Piwik\Metrics;
  17. use Piwik\Piwik;
  18. use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution as EvolutionViz;
  19. use Piwik\Url;
  20. use Piwik\ViewDataTable\Factory;
  21. /**
  22. * ROW EVOLUTION
  23. * The class handles the popover that shows the evolution of a singe row in a data table
  24. */
  25. class RowEvolution
  26. {
  27. /** The current site id */
  28. protected $idSite;
  29. /** The api method to get the data. Format: Plugin.apiAction */
  30. protected $apiMethod;
  31. /** The label of the requested row */
  32. protected $label;
  33. /** The requested period */
  34. protected $period;
  35. /** The requested date */
  36. protected $date;
  37. /** The request segment */
  38. protected $segment;
  39. /** The metrics that are available for the requested report and period */
  40. protected $availableMetrics;
  41. /** The name of the dimension of the current report */
  42. protected $dimension;
  43. /**
  44. * The data
  45. * @var \Piwik\DataTable
  46. */
  47. protected $dataTable;
  48. /** The label of the current record */
  49. protected $rowLabel;
  50. /** The icon of the current record */
  51. protected $rowIcon;
  52. /** The type of graph that has been requested last */
  53. protected $graphType;
  54. /** The metrics for the graph that has been requested last */
  55. protected $graphMetrics;
  56. /** Whether or not to show all metrics in the evolution graph when to popover opens */
  57. protected $initiallyShowAllMetrics = false;
  58. /**
  59. * The constructor
  60. * Initialize some local variables from the request
  61. * @param int $idSite
  62. * @param Date $date ($this->date from controller)
  63. * @param null|string $graphType
  64. * @throws Exception
  65. */
  66. public function __construct($idSite, $date, $graphType = null)
  67. {
  68. $this->apiMethod = Common::getRequestVar('apiMethod', '', 'string');
  69. if (empty($this->apiMethod)) throw new Exception("Parameter apiMethod not set.");
  70. $this->label = ResponseBuilder::getLabelFromRequest($_GET);
  71. if(!is_array($this->label)) {
  72. throw new Exception("Expected label to be an array, got instead: " . $this->label);
  73. }
  74. $this->label = $this->label[0];
  75. if ($this->label === '') throw new Exception("Parameter label not set.");
  76. $this->period = Common::getRequestVar('period', '', 'string');
  77. if (empty($this->period)) throw new Exception("Parameter period not set.");
  78. $this->idSite = $idSite;
  79. $this->graphType = $graphType;
  80. if ($this->period != 'range') {
  81. // handle day, week, month and year: display last X periods
  82. $end = $date->toString();
  83. list($this->date, $lastN) = EvolutionViz::getDateRangeAndLastN($this->period, $end);
  84. }
  85. $this->segment = \Piwik\API\Request::getRawSegmentFromRequest();
  86. $this->loadEvolutionReport();
  87. }
  88. /**
  89. * Render the popover
  90. * @param \Piwik\Plugins\CoreHome\Controller $controller
  91. * @param View (the popover_rowevolution template)
  92. */
  93. public function renderPopover($controller, $view)
  94. {
  95. // render main evolution graph
  96. $this->graphType = 'graphEvolution';
  97. $this->graphMetrics = $this->availableMetrics;
  98. $view->graph = $controller->getRowEvolutionGraph($fetch = true, $rowEvolution = $this);
  99. // render metrics overview
  100. $view->metrics = $this->getMetricsToggles();
  101. // available metrics text
  102. $metricsText = Piwik::translate('RowEvolution_AvailableMetrics');
  103. $popoverTitle = '';
  104. if ($this->rowLabel) {
  105. $icon = $this->rowIcon ? '<img src="' . $this->rowIcon . '" alt="">' : '';
  106. $metricsText = sprintf(Piwik::translate('RowEvolution_MetricsFor'), $this->dimension . ': ' . $icon . ' ' . $this->rowLabel);
  107. $popoverTitle = $icon . ' ' . $this->rowLabel;
  108. }
  109. $view->availableMetricsText = $metricsText;
  110. $view->popoverTitle = $popoverTitle;
  111. return $view->render();
  112. }
  113. protected function loadEvolutionReport($column = false)
  114. {
  115. list($apiModule, $apiAction) = explode('.', $this->apiMethod);
  116. $parameters = array(
  117. 'method' => 'API.getRowEvolution',
  118. 'label' => $this->label,
  119. 'apiModule' => $apiModule,
  120. 'apiAction' => $apiAction,
  121. 'idSite' => $this->idSite,
  122. 'period' => $this->period,
  123. 'date' => $this->date,
  124. 'format' => 'original',
  125. 'serialize' => '0'
  126. );
  127. if (!empty($this->segment)) {
  128. $parameters['segment'] = $this->segment;
  129. }
  130. if ($column !== false) {
  131. $parameters['column'] = $column;
  132. }
  133. $url = Url::getQueryStringFromParameters($parameters);
  134. $request = new Request($url);
  135. $report = $request->process();
  136. $this->extractEvolutionReport($report);
  137. }
  138. protected function extractEvolutionReport($report)
  139. {
  140. $this->dataTable = $report['reportData'];
  141. $this->rowLabel = $this->extractPrettyLabel($report);
  142. $this->rowIcon = !empty($report['logo']) ? $report['logo'] : false;
  143. $this->availableMetrics = $report['metadata']['metrics'];
  144. $this->dimension = $report['metadata']['dimension'];
  145. }
  146. /**
  147. * Generic method to get an evolution graph or a sparkline for the row evolution popover.
  148. * Do as much as possible from outside the controller.
  149. * @param string|bool $graphType
  150. * @param array|bool $metrics
  151. * @return Factory
  152. */
  153. public function getRowEvolutionGraph($graphType = false, $metrics = false)
  154. {
  155. // set up the view data table
  156. $view = Factory::build($graphType ? : $this->graphType, $this->apiMethod,
  157. $controllerAction = 'CoreHome.getRowEvolutionGraph', $forceDefault = true);
  158. $view->setDataTable($this->dataTable);
  159. if (!empty($this->graphMetrics)) { // In row Evolution popover, this is empty
  160. $view->config->columns_to_display = array_keys($metrics ? : $this->graphMetrics);
  161. }
  162. $view->config->show_goals = false;
  163. $view->config->show_all_views_icons = false;
  164. $view->config->show_active_view_icon = false;
  165. $view->config->show_related_reports = false;
  166. $view->config->show_series_picker = false;
  167. $view->config->show_footer_message = false;
  168. foreach ($this->availableMetrics as $metric => $metadata) {
  169. $view->config->translations[$metric] = $metadata['name'];
  170. }
  171. $view->config->external_series_toggle = 'RowEvolutionSeriesToggle';
  172. $view->config->external_series_toggle_show_all = $this->initiallyShowAllMetrics;
  173. return $view;
  174. }
  175. /**
  176. * Prepare metrics toggles with spark lines
  177. * @return array
  178. */
  179. protected function getMetricsToggles()
  180. {
  181. $i = 0;
  182. $metrics = array();
  183. foreach ($this->availableMetrics as $metric => $metricData) {
  184. $unit = Metrics::getUnit($metric, $this->idSite);
  185. $change = isset($metricData['change']) ? $metricData['change'] : false;
  186. list($first, $last) = $this->getFirstAndLastDataPointsForMetric($metric);
  187. $details = Piwik::translate('RowEvolution_MetricBetweenText', array($first, $last));
  188. if ($change !== false) {
  189. $lowerIsBetter = Metrics::isLowerValueBetter($metric);
  190. if (substr($change, 0, 1) == '+') {
  191. $changeClass = $lowerIsBetter ? 'bad' : 'good';
  192. $changeImage = $lowerIsBetter ? 'arrow_up_red' : 'arrow_up';
  193. } else if (substr($change, 0, 1) == '-') {
  194. $changeClass = $lowerIsBetter ? 'good' : 'bad';
  195. $changeImage = $lowerIsBetter ? 'arrow_down_green' : 'arrow_down';
  196. } else {
  197. $changeClass = 'neutral';
  198. $changeImage = false;
  199. }
  200. $change = '<span class="' . $changeClass . '">'
  201. . ($changeImage ? '<img src="plugins/MultiSites/images/' . $changeImage . '.png" /> ' : '')
  202. . $change . '</span>';
  203. $details .= ', ' . Piwik::translate('RowEvolution_MetricChangeText', $change);
  204. }
  205. // set metric min/max text (used as tooltip for details)
  206. $max = isset($metricData['max']) ? $metricData['max'] : 0;
  207. $min = isset($metricData['min']) ? $metricData['min'] : 0;
  208. $min .= $unit;
  209. $max .= $unit;
  210. $minmax = Piwik::translate('RowEvolution_MetricMinMax', array($metricData['name'], $min, $max));
  211. $newMetric = array(
  212. 'label' => $metricData['name'],
  213. 'details' => $details,
  214. 'minmax' => $minmax,
  215. 'sparkline' => $this->getSparkline($metric),
  216. );
  217. // Multi Rows, each metric can be for a particular row and display an icon
  218. if (!empty($metricData['logo'])) {
  219. $newMetric['logo'] = $metricData['logo'];
  220. }
  221. $metrics[] = $newMetric;
  222. $i++;
  223. }
  224. return $metrics;
  225. }
  226. /** Get the img tag for a sparkline showing a single metric */
  227. protected function getSparkline($metric)
  228. {
  229. // sparkline is always echoed, so we need to buffer the output
  230. $view = $this->getRowEvolutionGraph($graphType = 'sparkline', $metrics = array($metric => $metric));
  231. ob_start();
  232. $view->render();
  233. $spark = ob_get_contents();
  234. ob_end_clean();
  235. // undo header change by sparkline renderer
  236. header('Content-type: text/html');
  237. // base64 encode the image and put it in an img tag
  238. $spark = base64_encode($spark);
  239. return '<img src="data:image/png;base64,' . $spark . '" />';
  240. }
  241. /** Use the available metrics for the metrics of the last requested graph. */
  242. public function useAvailableMetrics()
  243. {
  244. $this->graphMetrics = $this->availableMetrics;
  245. }
  246. private function getFirstAndLastDataPointsForMetric($metric)
  247. {
  248. $first = 0;
  249. $firstTable = $this->dataTable->getFirstRow();
  250. if (!empty($firstTable)) {
  251. $row = $firstTable->getFirstRow();
  252. if (!empty($row)) {
  253. $first = floatval($row->getColumn($metric));
  254. }
  255. }
  256. $last = 0;
  257. $lastTable = $this->dataTable->getLastRow();
  258. if (!empty($lastTable)) {
  259. $row = $lastTable->getFirstRow();
  260. if (!empty($row)) {
  261. $last = floatval($row->getColumn($metric));
  262. }
  263. }
  264. return array($first, $last);
  265. }
  266. /**
  267. * @param $report
  268. * @return string
  269. */
  270. protected function extractPrettyLabel($report)
  271. {
  272. // By default, use the specified label
  273. $rowLabel = Common::sanitizeInputValue($report['label']);
  274. $rowLabel = str_replace('/', '<wbr>/', str_replace('&', '<wbr>&', $rowLabel ));
  275. // If the dataTable specifies a label_html, use this instead
  276. /** @var $dataTableMap \Piwik\DataTable\Map */
  277. $dataTableMap = $report['reportData'];
  278. $labelPretty = $dataTableMap->getColumn('label_html');
  279. $labelPretty = array_filter($labelPretty, 'strlen');
  280. $labelPretty = current($labelPretty);
  281. if(!empty($labelPretty)) {
  282. return $labelPretty;
  283. }
  284. return $rowLabel;
  285. }
  286. }