1<?php
2/**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 */
8
9namespace Piwik\Plugins\Goals\Visualizations;
10
11use Piwik\API\DataTablePostProcessor;
12use Piwik\API\Request;
13use Piwik\Common;
14use Piwik\DataTable;
15use Piwik\DataTable\Filter\AddColumnsProcessedMetricsGoal;
16use Piwik\Piwik;
17use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
18use Piwik\Plugins\Goals\API as APIGoals;
19use Piwik\Site;
20use Piwik\View;
21
22require_once PIWIK_INCLUDE_PATH . '/core/Twig.php';
23
24/**
25 * DataTable Visualization that derives from HtmlTable and sets show_goals_columns to true.
26 */
27class Goals extends HtmlTable
28{
29    const ID = 'tableGoals';
30    const FOOTER_ICON       = 'icon-goal';
31    const FOOTER_ICON_TITLE = 'General_DisplayTableWithGoalMetrics';
32
33    public function beforeLoadDataTable()
34    {
35        parent::beforeLoadDataTable();
36
37        $this->config->show_totals_row = false;
38
39        if ($this->config->disable_subtable_when_show_goals) {
40            $this->config->subtable_controller_action = null;
41        }
42
43        $this->setShowGoalsColumnsProperties();
44    }
45
46    public function beforeRender()
47    {
48        $this->config->show_totals_row = false;
49        $this->config->show_goals = true;
50        $this->config->show_goals_columns  = true;
51        $this->config->datatable_css_class = 'dataTableVizGoals';
52        $this->config->show_exclude_low_population = true;
53
54        $this->config->metrics_documentation['nb_visits'] = Piwik::translate('Goals_ColumnVisits');
55
56        if (1 == Common::getRequestVar('documentationForGoalsPage', 0, 'int')) {
57            // TODO: should not use query parameter
58            $this->config->documentation = Piwik::translate('Goals_ConversionByTypeReportDocumentation',
59                array('<br />', '<br />', '<a href="https://matomo.org/docs/tracking-goals-web-analytics/" rel="noreferrer noopener" target="_blank">', '</a>'));
60        }
61
62        parent::beforeRender();
63    }
64
65    private function setShowGoalsColumnsProperties()
66    {
67        // set view properties based on goal requested
68        $idSite = Common::getRequestVar('idSite', null, 'int');
69        $idGoal = Common::getRequestVar('idGoal', AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW, 'string');
70
71        $goalsToProcess = null;
72        if (Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER == $idGoal) {
73            $this->setPropertiesForEcommerceView();
74
75            $goalsToProcess = array($idGoal);
76        } else if (AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE == $idGoal) {
77            $this->setPropertiesForGoals($idSite, 'all');
78
79            $goalsToProcess = $this->getAllGoalIds($idSite);
80        } else if (AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW == $idGoal) {
81            $this->setPropertiesForGoalsOverview($idSite);
82
83            $goalsToProcess = $this->getAllGoalIds($idSite);
84        } else {
85            $this->setPropertiesForGoals($idSite, array($idGoal));
86
87            $goalsToProcess = array($idGoal);
88        }
89
90        // add goals columns
91        $this->requestConfig->request_parameters_to_modify['filter_update_columns_when_show_all_goals'] = $idGoal;
92        $this->requestConfig->request_parameters_to_modify['filter_show_goal_columns_process_goals'] = implode(',', $goalsToProcess);
93    }
94
95    private function setPropertiesForEcommerceView()
96    {
97        $this->requestConfig->filter_sort_column = 'goal_ecommerceOrder_revenue';
98        $this->requestConfig->filter_sort_order = 'desc';
99
100        $this->config->columns_to_display = array(
101            'label', 'nb_visits', 'goal_ecommerceOrder_nb_conversions', 'goal_ecommerceOrder_revenue',
102            'goal_ecommerceOrder_conversion_rate', 'goal_ecommerceOrder_avg_order_revenue', 'goal_ecommerceOrder_items',
103            'goal_ecommerceOrder_revenue_per_visit'
104        );
105
106        $this->config->translations = array_merge($this->config->translations, array(
107            'goal_ecommerceOrder_nb_conversions'    => Piwik::translate('General_EcommerceOrders'),
108            'goal_ecommerceOrder_revenue'           => Piwik::translate('General_TotalRevenue'),
109            'goal_ecommerceOrder_revenue_per_visit' => Piwik::translate('General_ColumnValuePerVisit')
110        ));
111
112        $goalName = Piwik::translate('General_EcommerceOrders');
113        $this->config->metrics_documentation['revenue_per_visit'] =
114            Piwik::translate('Goals_ColumnRevenuePerVisitDocumentation', $goalName);
115    }
116
117    private function setPropertiesForGoalsOverview($idSite)
118    {
119        $allGoals = $this->getGoals($idSite);
120
121        // set view properties
122        $this->config->columns_to_display = array('label', 'nb_visits');
123
124        foreach ($allGoals as $goal) {
125            $column        = "goal_{$goal['idgoal']}_conversion_rate";
126            $this->config->columns_to_display[]  = $column;
127        }
128
129        $this->config->columns_to_display[] = 'revenue_per_visit';
130    }
131
132    private function setPropertiesForGoals($idSite, $idGoals)
133    {
134        $allGoals = $this->getGoals($idSite);
135
136        if ('all' == $idGoals) {
137            $idGoals = array_keys($allGoals);
138        } else {
139            // only sort by a goal's conversions if not showing all goals (for FULL_REPORT)
140            $this->requestConfig->filter_sort_column = 'goal_' . reset($idGoals) . '_nb_conversions';
141            $this->requestConfig->filter_sort_order  = 'desc';
142        }
143
144        $this->config->columns_to_display = array('label', 'nb_visits');
145
146        $goalColumnTemplates = array(
147            'goal_%s_nb_conversions',
148            'goal_%s_conversion_rate',
149            'goal_%s_revenue',
150            'goal_%s_revenue_per_visit',
151        );
152
153        // set columns to display (columns of same type but different goals will be next to each other,
154        // ie, goal_0_nb_conversions, goal_1_nb_conversions, etc.)
155        foreach ($goalColumnTemplates as $columnTemplate) {
156            foreach ($idGoals as $idGoal) {
157                $this->config->columns_to_display[] = sprintf($columnTemplate, $idGoal);
158            }
159        }
160
161        $this->config->columns_to_display[] = 'revenue_per_visit';
162    }
163
164    private $goalsForCurrentSite = null;
165
166    private function getGoals($idSite)
167    {
168        if ($this->goalsForCurrentSite === null) {
169            // get all goals to display info for
170            $allGoals = array();
171
172            // add the ecommerce goal if ecommerce is enabled for the site
173            if (Site::isEcommerceEnabledFor($idSite)) {
174                $ecommerceGoal = array(
175                    'idgoal'      => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER,
176                    'name'        => Piwik::translate('Goals_EcommerceOrder'),
177                    'quoted_name' => false
178                );
179                $allGoals[$ecommerceGoal['idgoal']] = $ecommerceGoal;
180            }
181
182            // add the site's goals (and escape all goal names)
183            $siteGoals = Request::processRequest('Goals.getGoals', ['idSite' => $idSite, 'filter_limit' => '-1'], $default = []);
184
185            foreach ($siteGoals as &$goal) {
186                $goal['name'] = Common::sanitizeInputValue($goal['name']);
187
188                $goal['quoted_name'] = '"' . $goal['name'] . '"';
189                $allGoals[$goal['idgoal']] = $goal;
190            }
191
192            $this->goalsForCurrentSite = $allGoals;
193        }
194
195        return $this->goalsForCurrentSite;
196    }
197
198    private function getAllGoalIds($idSite)
199    {
200        $allGoals = $this->getGoals($idSite);
201        return array_map(function ($data) {
202            return $data['idgoal'];
203        }, $allGoals);
204    }
205}
206