PageRenderTime 81ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/plugins/Goals/Controller.php

https://github.com/quarkness/piwik
PHP | 489 lines | 413 code | 48 blank | 28 comment | 21 complexity | c036ef6d5f1263df425a0613adf2ef04 MD5 | raw file
  1. <?php
  2. /**
  3. * Piwik - Open source web analytics
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. * @version $Id$
  8. *
  9. * @category Piwik_Plugins
  10. * @package Piwik_Goals
  11. */
  12. /**
  13. *
  14. * @package Piwik_Goals
  15. */
  16. class Piwik_Goals_Controller extends Piwik_Controller
  17. {
  18. const CONVERSION_RATE_PRECISION = 1;
  19. protected $goalColumnNameToLabel = array(
  20. 'avg_order_revenue' => 'General_AverageOrderValue',
  21. 'nb_conversions' => 'Goals_ColumnConversions',
  22. 'conversion_rate'=> 'General_ColumnConversionRate',
  23. 'revenue' => 'General_TotalRevenue',
  24. 'items' => 'General_PurchasedProducts',
  25. );
  26. private function formatConversionRate($conversionRate)
  27. {
  28. return sprintf('%.' . self::CONVERSION_RATE_PRECISION . 'f%%', $conversionRate);
  29. }
  30. public function __construct()
  31. {
  32. parent::__construct();
  33. $this->idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
  34. $this->goals = Piwik_Goals_API::getInstance()->getGoals($this->idSite);
  35. foreach($this->goals as &$goal)
  36. {
  37. $goal['name'] = Piwik_Common::sanitizeInputValue($goal['name']);
  38. if(isset($goal['pattern']))
  39. {
  40. $goal['pattern'] = Piwik_Common::sanitizeInputValue($goal['pattern']);
  41. }
  42. }
  43. }
  44. public function widgetGoalReport()
  45. {
  46. $view = $this->getGoalReportView($idGoal = Piwik_Common::getRequestVar('idGoal', null, 'string'));
  47. $view->displayFullReport = false;
  48. echo $view->render();
  49. }
  50. public function goalReport()
  51. {
  52. $view = $this->getGoalReportView($idGoal = Piwik_Common::getRequestVar('idGoal', null, 'string'));
  53. $view->displayFullReport = true;
  54. $view->goalDimensions = Piwik_Goals::getReportsWithGoalMetrics();
  55. echo $view->render();
  56. }
  57. public function ecommerceReport()
  58. {
  59. $view = $this->getGoalReportView($idGoal = Piwik_Archive::LABEL_ECOMMERCE_ORDER);
  60. $view->displayFullReport = true;
  61. $view->goalDimensions = Piwik_Goals::getReportsWithGoalMetrics();
  62. echo $view->render();
  63. }
  64. protected function getItemsView($fetch, $type, $function, $api, $abandonedCart = false)
  65. {
  66. $saveGET = $_GET;
  67. $label = Piwik_Translate($type);
  68. $abandonedCart = Piwik_Common::getRequestVar('viewDataTable', 'ecommerceOrder', 'string') == 'ecommerceAbandonedCart';
  69. // Products in Ecommerce Orders
  70. if($abandonedCart === false)
  71. {
  72. $view = new Piwik_ViewDataTable_HtmlTable_EcommerceOrder();
  73. $columns = Piwik_Goals::getProductReportColumns();
  74. $view->setMetricDocumentation('revenue', Piwik_Translate('Goals_ColumnRevenueDocumentation', Piwik_Translate('Goals_DocumentationRevenueGeneratedByProductSales')));
  75. $view->setMetricDocumentation('quantity', Piwik_Translate('Goals_ColumnQuantityDocumentation', $label));
  76. $view->setMetricDocumentation('orders', Piwik_Translate('Goals_ColumnOrdersDocumentation', $label));
  77. $view->setMetricDocumentation('avg_price', Piwik_Translate('Goals_ColumnAveragePriceDocumentation', $label));
  78. $view->setMetricDocumentation('avg_quantity', Piwik_Translate('Goals_ColumnAverageQuantityDocumentation', $label));
  79. $view->setMetricDocumentation('nb_visits', Piwik_Translate('Goals_ColumnVisitsProductDocumentation', $label));
  80. $view->setMetricDocumentation('conversion_rate', Piwik_Translate('Goals_ColumnConversionRateProductDocumentation', $label));
  81. }
  82. // Products in Abandoned Carts
  83. else
  84. {
  85. $view = new Piwik_ViewDataTable_HtmlTable_EcommerceAbandonedCart();
  86. $columns = Piwik_Goals::getProductReportColumns();
  87. $columns['abandoned_carts'] = Piwik_Translate('General_AbandonedCarts');
  88. $columns['revenue'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('General_ProductRevenue'));
  89. $columns['quantity'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('General_Quantity'));
  90. $columns['avg_quantity'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('General_AverageQuantity'));
  91. unset($columns['orders']);
  92. unset($columns['nb_visits']);
  93. unset($columns['conversion_rate']);
  94. $_GET['abandonedCarts'] = 1;
  95. }
  96. $view->init( $this->pluginName, $function, $api );
  97. $view->enableShowEcommerce();
  98. $view->disableShowAllViewsIcons();
  99. $view->disableShowTable();
  100. $view->disableExcludeLowPopulation();
  101. $view->disableShowAllColumns();
  102. $this->setPeriodVariablesView($view);
  103. $view->setLimit( 10 );
  104. $view->setColumnsTranslations(array_merge(
  105. array('label' => $label),
  106. $columns
  107. ));
  108. $columnsToDisplay = array_merge(array('label'), array_keys($columns));
  109. $view->setColumnsToDisplay($columnsToDisplay);
  110. $view->setSortedColumn('revenue', 'desc');
  111. foreach(array('revenue', 'avg_price') as $column)
  112. {
  113. $view->queueFilter('ColumnCallbackReplace', array($column, array("Piwik", "getPrettyMoney"), array($this->idSite)));
  114. }
  115. $return = $this->renderView($view, $fetch);
  116. $_GET = $saveGET;
  117. return $return;
  118. }
  119. public function getItemsSku($fetch = false)
  120. {
  121. return $this->getItemsView($fetch, 'Goals_ProductSKU', __FUNCTION__, "Goals.getItemsSku");
  122. }
  123. public function getItemsName($fetch = false)
  124. {
  125. return $this->getItemsView($fetch, 'Goals_ProductName', __FUNCTION__, "Goals.getItemsName");
  126. }
  127. public function getItemsCategory($fetch = false)
  128. {
  129. return $this->getItemsView($fetch, 'Goals_ProductCategory', __FUNCTION__, "Goals.getItemsCategory");
  130. }
  131. public function getEcommerceLog($fetch = false)
  132. {
  133. $saveGET = $_GET;
  134. $_GET['filterEcommerce'] = 1;
  135. $_GET['widget'] = 1;
  136. $_GET['segment'] = 'visitEcommerceStatus!=none';
  137. $output = Piwik_FrontController::getInstance()->dispatch('Live', 'getVisitorLog', array($fetch));
  138. $_GET = $saveGET;
  139. return $output;
  140. }
  141. protected function getGoalReportView($idGoal = false)
  142. {
  143. $view = Piwik_View::factory('single_goal');
  144. if($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
  145. {
  146. $goalDefinition['name'] = Piwik_Translate('Goals_Ecommerce');
  147. $goalDefinition['allow_multiple'] = true;
  148. $view->ecommerce = true;
  149. }
  150. else
  151. {
  152. if(!isset($this->goals[$idGoal]))
  153. {
  154. Piwik::redirectToModule('Goals', 'index', array('idGoal' => null));
  155. }
  156. $goalDefinition = $this->goals[$idGoal];
  157. }
  158. $this->setGeneralVariablesView($view);
  159. $goal = $this->getMetricsForGoal($idGoal);
  160. foreach($goal as $name => $value)
  161. {
  162. $view->$name = $value;
  163. }
  164. if($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
  165. {
  166. $goal = $this->getMetricsForGoal(Piwik_Archive::LABEL_ECOMMERCE_CART);
  167. foreach($goal as $name => $value)
  168. {
  169. $name = 'cart_'.$name;
  170. $view->$name = $value;
  171. }
  172. }
  173. $view->idGoal = $idGoal;
  174. $view->goalName = $goalDefinition['name'];
  175. $view->goalAllowMultipleConversionsPerVisit = $goalDefinition['allow_multiple'];
  176. $view->graphEvolution = $this->getEvolutionGraph(true, array('nb_conversions'), $idGoal);
  177. $view->nameGraphEvolution = 'GoalsgetEvolutionGraph'.$idGoal;
  178. $view->topDimensions = $this->getTopDimensions($idGoal);
  179. // conversion rate for new and returning visitors
  180. $segment = 'visitorType==returning,visitorType==returningCustomer';
  181. $conversionRateReturning = Piwik_Goals_API::getInstance()->getConversionRate($this->idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), $segment, $idGoal);
  182. $view->conversion_rate_returning = $this->formatConversionRate($conversionRateReturning);
  183. $segment = 'visitorType==new';
  184. $conversionRateNew = Piwik_Goals_API::getInstance()->getConversionRate($this->idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), $segment, $idGoal);
  185. $view->conversion_rate_new = $this->formatConversionRate($conversionRateNew);
  186. return $view;
  187. }
  188. public function index()
  189. {
  190. $view = $this->getOverviewView();
  191. $view->goalsJSON = Piwik_Common::json_encode($this->goals);
  192. $view->goalDimensions = Piwik_Goals::getReportsWithGoalMetrics();
  193. $view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
  194. $view->ecommerceEnabled = $this->site->isEcommerceEnabled();
  195. $view->displayFullReport = true;
  196. echo $view->render();
  197. }
  198. public function widgetGoalsOverview( )
  199. {
  200. $view = $this->getOverviewView();
  201. $view->displayFullReport = false;
  202. echo $view->render();
  203. }
  204. protected function getOverviewView()
  205. {
  206. $view = Piwik_View::factory('overview');
  207. $this->setGeneralVariablesView($view);
  208. $view->graphEvolution = $this->getEvolutionGraph(true, array('nb_conversions'));
  209. $view->nameGraphEvolution = 'GoalsgetEvolutionGraph';
  210. // sparkline for the historical data of the above values
  211. $view->urlSparklineConversions = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_conversions')));
  212. $view->urlSparklineConversionRate = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('conversion_rate')));
  213. $view->urlSparklineRevenue = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('revenue')));
  214. // Pass empty idGoal will return Goal overview
  215. $request = new Piwik_API_Request("method=Goals.get&format=original&idGoal=");
  216. $datatable = $request->process();
  217. $dataRow = $datatable->getFirstRow();
  218. $view->nb_conversions = $dataRow->getColumn('nb_conversions');
  219. $view->nb_visits_converted = $dataRow->getColumn('nb_visits_converted');
  220. $view->conversion_rate = $this->formatConversionRate($dataRow->getColumn('conversion_rate'));
  221. $view->revenue = $dataRow->getColumn('revenue');
  222. $goalMetrics = array();
  223. foreach($this->goals as $idGoal => $goal)
  224. {
  225. $goalMetrics[$idGoal] = $this->getMetricsForGoal($idGoal);
  226. $goalMetrics[$idGoal]['name'] = $goal['name'];
  227. $goalMetrics[$idGoal]['goalAllowMultipleConversionsPerVisit'] = $goal['allow_multiple'];
  228. }
  229. $view->goalMetrics = $goalMetrics;
  230. $view->goals = $this->goals;
  231. return $view;
  232. }
  233. public function getLastNbConversionsGraph( $fetch = false )
  234. {
  235. $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getConversions');
  236. return $this->renderView($view, $fetch);
  237. }
  238. public function getLastConversionRateGraph( $fetch = false )
  239. {
  240. $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getConversionRate');
  241. return $this->renderView($view, $fetch);
  242. }
  243. public function getLastRevenueGraph( $fetch = false )
  244. {
  245. $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getRevenue');
  246. return $this->renderView($view, $fetch);
  247. }
  248. public function addNewGoal()
  249. {
  250. $view = Piwik_View::factory('add_new_goal');
  251. $this->setGeneralVariablesView($view);
  252. $view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
  253. $view->onlyShowAddNewGoal = true;
  254. echo $view->render();
  255. }
  256. public function getEvolutionGraph( $fetch = false, $columns = false, $idGoal = false)
  257. {
  258. if(empty($columns))
  259. {
  260. $columns = Piwik_Common::getRequestVar('columns');
  261. $columns = Piwik::getArrayFromApiParameter($columns);
  262. }
  263. $columns = !is_array($columns) ? array($columns) : $columns;
  264. if(empty($idGoal))
  265. {
  266. $idGoal = Piwik_Common::getRequestVar('idGoal', false, 'string');
  267. }
  268. $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.get');
  269. $view->setParametersToModify(array('idGoal' => $idGoal));
  270. $nameToLabel = $this->goalColumnNameToLabel;
  271. if($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
  272. {
  273. $nameToLabel['nb_conversions'] = 'General_EcommerceOrders';
  274. }
  275. elseif($idGoal == Piwik_Archive::LABEL_ECOMMERCE_CART)
  276. {
  277. $nameToLabel['nb_conversions'] = Piwik_Translate('General_VisitsWith', Piwik_Translate('Goals_AbandonedCart'));
  278. $nameToLabel['conversion_rate'] = $nameToLabel['nb_conversions'];
  279. $nameToLabel['revenue'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('Goals_ColumnRevenue'));
  280. $nameToLabel['items'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('Goals_Products'));
  281. }
  282. $selectableColumns = array('nb_conversions', 'conversion_rate', 'revenue');
  283. if ($this->site->isEcommerceEnabled())
  284. {
  285. $selectableColumns[] = 'items';
  286. $selectableColumns[] = 'avg_order_revenue';
  287. }
  288. foreach(array_merge($columns, $selectableColumns) as $columnName)
  289. {
  290. $columnTranslation = '';
  291. // find the right translation for this column, eg. find 'revenue' if column is Goal_1_revenue
  292. foreach($nameToLabel as $metric => $metricTranslation)
  293. {
  294. if(strpos($columnName, $metric) !== false)
  295. {
  296. $columnTranslation = Piwik_Translate($metricTranslation);
  297. break;
  298. }
  299. }
  300. if(!empty($idGoal) && isset($this->goals[$idGoal]))
  301. {
  302. $goalName = $this->goals[$idGoal]['name'];
  303. $columnTranslation = "$columnTranslation (".Piwik_Translate('Goals_GoalX', "$goalName").")";
  304. }
  305. $view->setColumnTranslation($columnName, $columnTranslation);
  306. }
  307. $view->setColumnsToDisplay($columns);
  308. $view->setSelectableColumns($selectableColumns);
  309. $langString = $idGoal ? 'Goals_SingleGoalOverviewDocumentation' : 'Goals_GoalsOverviewDocumentation';
  310. $view->setReportDocumentation(Piwik_Translate($langString, '<br />'));
  311. return $this->renderView($view, $fetch);
  312. }
  313. protected function getTopDimensions($idGoal)
  314. {
  315. $columnNbConversions = 'goal_'.$idGoal.'_nb_conversions';
  316. $columnConversionRate = 'goal_'.$idGoal.'_conversion_rate';
  317. $topDimensionsToLoad = array(
  318. 'country' => 'UserCountry.getCountry',
  319. 'keyword' => 'Referers.getKeywords',
  320. 'website' => 'Referers.getWebsites',
  321. );
  322. $topDimensions = array();
  323. foreach($topDimensionsToLoad as $dimensionName => $apiMethod)
  324. {
  325. $request = new Piwik_API_Request("method=$apiMethod
  326. &format=original
  327. &filter_update_columns_when_show_all_goals=1
  328. &idGoal=". Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE ."
  329. &filter_sort_order=desc
  330. &filter_sort_column=$columnNbConversions
  331. &filter_limit=3");
  332. $datatable = $request->process();
  333. $topDimension = array();
  334. foreach($datatable->getRows() as $row)
  335. {
  336. $conversions = $row->getColumn($columnNbConversions);
  337. if($conversions > 0)
  338. {
  339. $topDimension[] = array (
  340. 'name' => $row->getColumn('label'),
  341. 'nb_conversions' => $conversions,
  342. 'conversion_rate' => $this->formatConversionRate($row->getColumn($columnConversionRate)),
  343. 'metadata' => $row->getMetadata(),
  344. );
  345. }
  346. }
  347. $topDimensions[$dimensionName] = $topDimension;
  348. }
  349. return $topDimensions;
  350. }
  351. protected function getMetricsForGoal($idGoal)
  352. {
  353. $request = new Piwik_API_Request("method=Goals.get&format=original&idGoal=$idGoal");
  354. $datatable = $request->process();
  355. $dataRow = $datatable->getFirstRow();
  356. $nbConversions = $dataRow->getColumn('nb_conversions');
  357. $nbVisitsConverted = $dataRow->getColumn('nb_visits_converted');
  358. // Backward compatibilty before 1.3, this value was not processed
  359. if(empty($nbVisitsConverted))
  360. {
  361. $nbVisitsConverted = $nbConversions;
  362. }
  363. $return = array (
  364. 'id' => $idGoal,
  365. 'nb_conversions' => $nbConversions,
  366. 'nb_visits_converted' => $nbVisitsConverted,
  367. 'conversion_rate' => $this->formatConversionRate($dataRow->getColumn('conversion_rate')),
  368. 'revenue' => $dataRow->getColumn('revenue'),
  369. 'urlSparklineConversions' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_conversions'), 'idGoal' => $idGoal)),
  370. 'urlSparklineConversionRate' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('conversion_rate'), 'idGoal' => $idGoal)),
  371. 'urlSparklineRevenue' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('revenue'), 'idGoal' => $idGoal)),
  372. );
  373. if($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER)
  374. {
  375. $return = array_merge($return, array(
  376. 'revenue_subtotal' => $dataRow->getColumn('revenue_subtotal'),
  377. 'revenue_tax' => $dataRow->getColumn('revenue_tax'),
  378. 'revenue_shipping' => $dataRow->getColumn('revenue_shipping'),
  379. 'revenue_discount' => $dataRow->getColumn('revenue_discount'),
  380. 'items' => $dataRow->getColumn('items'),
  381. 'avg_order_revenue' => $dataRow->getColumn('avg_order_revenue'),
  382. 'urlSparklinePurchasedProducts' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('items'), 'idGoal' => $idGoal)),
  383. 'urlSparklineAverageOrderValue' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('avg_order_revenue'), 'idGoal' => $idGoal)),
  384. ));
  385. }
  386. return $return;
  387. }
  388. /**
  389. * Gets the 'visits to conversion' report using the requested view type.
  390. */
  391. public function getVisitsUntilConversion( $fetch = false )
  392. {
  393. $view = Piwik_ViewDataTable::factory();
  394. $view->init($this->pluginName, __FUNCTION__, 'Goals.getVisitsUntilConversion', 'getVisitsUntilConversion');
  395. $view->disableSearchBox();
  396. $view->disableExcludeLowPopulation();
  397. $view->disableSubTableWhenShowGoals();
  398. $view->disableShowAllColumns();
  399. $view->setColumnsToDisplay( array('label','nb_conversions') );
  400. $view->setSortedColumn('label', 'asc');
  401. $view->setColumnTranslation('label', Piwik_Translate('Goals_VisitsUntilConv'));
  402. $view->setColumnTranslation('nb_conversions', Piwik_Translate('Goals_ColumnConversions'));
  403. $view->setLimit(count(Piwik_Goals::$visitCountRanges));
  404. $view->disableOffsetInformationAndPaginationControls();
  405. $view->disableShowAllViewsIcons();
  406. return $this->renderView($view, $fetch);
  407. }
  408. /**
  409. * Gets the 'days to conversion' report using the requested view type.
  410. */
  411. public function getDaysToConversion( $fetch = false )
  412. {
  413. $view = Piwik_ViewDataTable::factory();
  414. $view->init($this->pluginName, __FUNCTION__, 'Goals.getDaysToConversion', 'getDaysToConversion');
  415. $view->disableSearchBox();
  416. $view->disableExcludeLowPopulation();
  417. $view->disableSubTableWhenShowGoals();
  418. $view->disableShowAllColumns();
  419. $view->setColumnsToDisplay( array('label','nb_conversions') );
  420. $view->setSortedColumn('label', 'asc');
  421. $view->setColumnTranslation('label', Piwik_Translate('Goals_DaysToConv'));
  422. $view->setColumnTranslation('nb_conversions', Piwik_Translate('Goals_ColumnConversions'));
  423. $view->disableShowAllViewsIcons();
  424. $view->setLimit(count(Piwik_Goals::$daysToConvRanges));
  425. $view->disableOffsetInformationAndPaginationControls();
  426. return $this->renderView($view, $fetch);
  427. }
  428. }
  429. // Used so that the template knows which datatable is being currently viewed
  430. class Piwik_ViewDataTable_HtmlTable_EcommerceOrder extends Piwik_ViewDataTable_HtmlTable {
  431. protected function getViewDataTableId()
  432. {
  433. return Piwik_Archive::LABEL_ECOMMERCE_ORDER;
  434. }
  435. }
  436. class Piwik_ViewDataTable_HtmlTable_EcommerceAbandonedCart extends Piwik_ViewDataTable_HtmlTable {
  437. protected function getViewDataTableId()
  438. {
  439. return Piwik_Archive::LABEL_ECOMMERCE_CART;
  440. }
  441. }