PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.3.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.3.1
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / ViewDataTable / Manager.php
matomo / app / core / ViewDataTable Last commit date
Config.php 6 years ago Factory.php 6 years ago Manager.php 6 years ago Request.php 6 years ago RequestConfig.php 6 years ago
Manager.php
439 lines
1 <?php
2 /**
3 * Piwik - 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 */
9 namespace Piwik\ViewDataTable;
10
11 use Piwik\Cache;
12 use Piwik\Common;
13 use Piwik\Option;
14 use Piwik\Piwik;
15 use Piwik\Plugin\Report;
16 use Piwik\Plugin\ViewDataTable;
17 use Piwik\Plugins\CoreVisualizations\Visualizations\Cloud;
18 use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
19 use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Bar;
20 use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Pie;
21 use Piwik\Plugins\Goals\Visualizations\Goals;
22 use Piwik\Plugins\Insights\Visualizations\Insight;
23 use Piwik\Plugin\Manager as PluginManager;
24
25 /**
26 * ViewDataTable Manager.
27 *
28 */
29 class Manager
30 {
31 /**
32 * Returns the viewDataTable IDs of a visualization's class lineage.
33 *
34 * @see self::getVisualizationClassLineage
35 *
36 * @param string $klass The visualization class.
37 *
38 * @return array
39 */
40 public static function getIdsWithInheritance($klass)
41 {
42 $klasses = Common::getClassLineage($klass);
43
44 $result = array();
45 foreach ($klasses as $klass) {
46 try {
47 $result[] = $klass::getViewDataTableId();
48 } catch (\Exception $e) {
49 // in case $klass did not define an id: eg Plugin\ViewDataTable
50 continue;
51 }
52 }
53
54 return $result;
55 }
56
57 /**
58 * Returns all registered visualization classes. Uses the 'Visualization.getAvailable'
59 * event to retrieve visualizations.
60 *
61 * @return array Array mapping visualization IDs with their associated visualization classes.
62 * @throws \Exception If a visualization class does not exist or if a duplicate visualization ID
63 * is found.
64 * @return array
65 */
66 public static function getAvailableViewDataTables()
67 {
68 $cache = Cache::getTransientCache();
69 $cacheId = 'ViewDataTable.getAvailableViewDataTables';
70 $dataTables = $cache->fetch($cacheId);
71
72 if (!empty($dataTables)) {
73 return $dataTables;
74 }
75
76 $klassToExtend = '\\Piwik\\Plugin\\ViewDataTable';
77
78 /** @var string[] $visualizations */
79 $visualizations = PluginManager::getInstance()->findMultipleComponents('Visualizations', $klassToExtend);
80
81 $result = array();
82
83 foreach ($visualizations as $viz) {
84 if (!class_exists($viz)) {
85 throw new \Exception("Invalid visualization class '$viz' found in Visualization.getAvailableVisualizations.");
86 }
87
88 if (!is_subclass_of($viz, $klassToExtend)) {
89 throw new \Exception("ViewDataTable class '$viz' does not extend Plugin/ViewDataTable");
90 }
91
92 $vizId = $viz::getViewDataTableId();
93
94 if (isset($result[$vizId])) {
95 throw new \Exception("ViewDataTable ID '$vizId' is already in use!");
96 }
97
98 $result[$vizId] = $viz;
99 }
100
101 /**
102 * Triggered to filter available DataTable visualizations.
103 *
104 * Plugins that want to disable certain visualizations should subscribe to
105 * this event and remove visualizations from the incoming array.
106 *
107 * **Example**
108 *
109 * public function filterViewDataTable(&$visualizations)
110 * {
111 * unset($visualizations[HtmlTable::ID]);
112 * }
113 *
114 * @param array &$visualizations An array of all available visualizations indexed by visualization ID.
115 * @since Piwik 3.0.0
116 */
117 Piwik::postEvent('ViewDataTable.filterViewDataTable', array(&$result));
118
119 $cache->save($cacheId, $result);
120
121 return $result;
122 }
123
124 /**
125 * Returns all available visualizations that are not part of the CoreVisualizations plugin.
126 *
127 * @return array Array mapping visualization IDs with their associated visualization classes.
128 */
129 public static function getNonCoreViewDataTables()
130 {
131 $result = array();
132
133 foreach (static::getAvailableViewDataTables() as $vizId => $vizClass) {
134 if (false === strpos($vizClass, 'Piwik\\Plugins\\CoreVisualizations')
135 && false === strpos($vizClass, 'Piwik\\Plugins\\Goals\\Visualizations\\Goals')) {
136 $result[$vizId] = $vizClass;
137 }
138 }
139
140 return $result;
141 }
142
143 /**
144 * This method determines the default set of footer icons to display below a report.
145 *
146 * $result has the following format:
147 *
148 * ```
149 * array(
150 * array( // footer icon group 1
151 * 'class' => 'footerIconGroup1CssClass',
152 * 'buttons' => array(
153 * 'id' => 'myid',
154 * 'title' => 'My Tooltip',
155 * 'icon' => 'path/to/my/icon.png'
156 * )
157 * ),
158 * array( // footer icon group 2
159 * 'class' => 'footerIconGroup2CssClass',
160 * 'buttons' => array(...)
161 * ),
162 * ...
163 * )
164 * ```
165 */
166 public static function configureFooterIcons(ViewDataTable $view)
167 {
168 $result = array();
169
170 $normalViewIcons = self::getNormalViewIcons($view);
171
172 if (!empty($normalViewIcons['buttons'])) {
173 $result[] = $normalViewIcons;
174 }
175
176 // add insight views
177 $insightsViewIcons = array(
178 'class' => 'tableInsightViews',
179 'buttons' => array(),
180 );
181
182 $graphViewIcons = self::getGraphViewIcons($view);
183
184 $nonCoreVisualizations = static::getNonCoreViewDataTables();
185
186 foreach ($nonCoreVisualizations as $id => $klass) {
187 if ($klass::canDisplayViewDataTable($view) || $view::ID == $id) {
188 $footerIcon = static::getFooterIconFor($id);
189 if (Insight::ID == $footerIcon['id']) {
190 $insightsViewIcons['buttons'][] = static::getFooterIconFor($id);
191 } else {
192 $graphViewIcons['buttons'][] = static::getFooterIconFor($id);
193 }
194 }
195 }
196
197 $graphViewIcons['buttons'] = array_filter($graphViewIcons['buttons']);
198
199 if (!empty($insightsViewIcons['buttons'])
200 && $view->config->show_insights
201 ) {
202 $result[] = $insightsViewIcons;
203 }
204
205 if (!empty($graphViewIcons['buttons'])) {
206 $result[] = $graphViewIcons;
207 }
208
209 return $result;
210 }
211
212 /**
213 * Returns an array with information necessary for adding the viewDataTable to the footer.
214 *
215 * @param string $viewDataTableId
216 *
217 * @return array
218 */
219 private static function getFooterIconFor($viewDataTableId)
220 {
221 $tables = static::getAvailableViewDataTables();
222
223 if (!array_key_exists($viewDataTableId, $tables)) {
224 return;
225 }
226
227 $klass = $tables[$viewDataTableId];
228
229 return array(
230 'id' => $klass::getViewDataTableId(),
231 'title' => Piwik::translate($klass::FOOTER_ICON_TITLE),
232 'icon' => $klass::FOOTER_ICON,
233 );
234 }
235
236 public static function clearAllViewDataTableParameters()
237 {
238 Option::deleteLike('viewDataTableParameters_%');
239 }
240
241 public static function clearUserViewDataTableParameters($userLogin)
242 {
243 Option::deleteLike('viewDataTableParameters_' . $userLogin . '_%');
244 }
245
246 public static function getViewDataTableParameters($login, $controllerAction, $containerId = null)
247 {
248 $paramsKey = self::buildViewDataTableParametersOptionKey($login, $controllerAction, $containerId);
249 $params = Option::get($paramsKey);
250
251 if (empty($params)) {
252 return array();
253 }
254
255 $params = json_decode($params);
256 $params = (array) $params;
257
258 // when setting an invalid parameter, we silently ignore the invalid parameter and proceed
259 $params = self::removeNonOverridableParameters($controllerAction, $params);
260 self::unsetComparisonParams($params);
261
262 return $params;
263 }
264
265 /**
266 * Any parameter set here will be set into one of the following objects:
267 *
268 * - ViewDataTable.requestConfig[paramName]
269 * - ViewDataTable.config.custom_parameters[paramName]
270 * - ViewDataTable.config.custom_parameters[paramName]
271 *
272 * (see ViewDataTable::overrideViewPropertiesWithParams)
273
274 * @param $login
275 * @param $controllerAction
276 * @param $parametersToOverride
277 * @param string|null $containerId
278 * @throws \Exception
279 */
280 public static function saveViewDataTableParameters($login, $controllerAction, $parametersToOverride, $containerId = null)
281 {
282 $params = self::getViewDataTableParameters($login, $controllerAction);
283
284 self::unsetComparisonParams($params);
285
286 foreach ($parametersToOverride as $key => $value) {
287 if ($key === 'viewDataTable'
288 && !empty($params[$key])
289 && $params[$key] !== $value) {
290 if (!empty($params['columns'])) {
291 unset($params['columns']);
292 }
293 if (!empty($params['columns_to_display'])) {
294 unset($params['columns_to_display']);
295 }
296 }
297
298 $params[$key] = $value;
299 }
300
301 $paramsKey = self::buildViewDataTableParametersOptionKey($login, $controllerAction, $containerId);
302
303 // when setting an invalid parameter, we fail and let user know
304 self::errorWhenSettingNonOverridableParameter($controllerAction, $params);
305
306 Option::set($paramsKey, json_encode($params));
307 }
308
309 private static function buildViewDataTableParametersOptionKey($login, $controllerAction, $containerId)
310 {
311 $result = sprintf('viewDataTableParameters_%s_%s', $login, $controllerAction);
312 if (!empty($containerId)) {
313 $result .= '_' . $containerId;
314 }
315 return $result;
316 }
317
318 /**
319 * Display a meaningful error message when any invalid parameter is being set.
320 *
321 * @param $params
322 * @throws
323 */
324 private static function errorWhenSettingNonOverridableParameter($controllerAction, $params)
325 {
326 $viewDataTable = self::makeTemporaryViewDataTableInstance($controllerAction, $params);
327 $viewDataTable->throwWhenSettingNonOverridableParameter($params);
328 }
329
330 private static function removeNonOverridableParameters($controllerAction, $params)
331 {
332 $viewDataTable = self::makeTemporaryViewDataTableInstance($controllerAction, $params);
333 $nonOverridableParams = $viewDataTable->getNonOverridableParams($params);
334
335 foreach($params as $key => $value) {
336 if(in_array($key, $nonOverridableParams)) {
337 unset($params[$key]);
338 }
339 }
340 return $params;
341 }
342
343 /**
344 * @param $controllerAction
345 * @param $params
346 * @return ViewDataTable
347 * @throws \Exception
348 */
349 private static function makeTemporaryViewDataTableInstance($controllerAction, $params)
350 {
351 $report = new Report();
352 $viewDataTableType = isset($params['viewDataTable']) ? $params['viewDataTable'] : $report->getDefaultTypeViewDataTable();
353
354 $apiAction = $controllerAction;
355 $loadViewDataTableParametersForUser = false;
356 $viewDataTable = Factory::build($viewDataTableType, $apiAction, $controllerAction, $forceDefault = false, $loadViewDataTableParametersForUser);
357 return $viewDataTable;
358 }
359
360 private static function getNormalViewIcons(ViewDataTable $view)
361 {
362 // add normal view icons (eg, normal table, all columns, goals)
363 $normalViewIcons = array(
364 'class' => 'tableAllColumnsSwitch',
365 'buttons' => array(),
366 );
367
368 if ($view->config->show_table) {
369 $normalViewIcons['buttons'][] = static::getFooterIconFor(HtmlTable::ID);
370 }
371
372 if ($view->config->show_table_all_columns) {
373 $normalViewIcons['buttons'][] = static::getFooterIconFor(HtmlTable\AllColumns::ID);
374 }
375
376 if ($view->config->show_goals) {
377 $goalButton = static::getFooterIconFor(Goals::ID);
378 if (Common::getRequestVar('idGoal', false) == 'ecommerceOrder') {
379 $goalButton['icon'] = 'icon-ecommerce-order';
380 }
381
382 $normalViewIcons['buttons'][] = $goalButton;
383 }
384
385 if ($view->config->show_ecommerce) {
386 $normalViewIcons['buttons'][] = array(
387 'id' => 'ecommerceOrder',
388 'title' => Piwik::translate('General_EcommerceOrders'),
389 'icon' => 'icon-ecommerce-order',
390 'text' => Piwik::translate('General_EcommerceOrders')
391 );
392
393 $normalViewIcons['buttons'][] = array(
394 'id' => 'ecommerceAbandonedCart',
395 'title' => Piwik::translate('General_AbandonedCarts'),
396 'icon' => 'icon-ecommerce-abandoned-cart',
397 'text' => Piwik::translate('General_AbandonedCarts')
398 );
399 }
400
401 $normalViewIcons['buttons'] = array_filter($normalViewIcons['buttons']);
402
403 return $normalViewIcons;
404 }
405
406 private static function getGraphViewIcons(ViewDataTable $view)
407 {
408 // add graph views
409 $graphViewIcons = array(
410 'class' => 'tableGraphViews',
411 'buttons' => array(),
412 );
413
414 if ($view->config->show_all_views_icons) {
415 if ($view->config->show_bar_chart) {
416 $graphViewIcons['buttons'][] = static::getFooterIconFor(Bar::ID);
417 }
418
419 if ($view->config->show_pie_chart) {
420 $graphViewIcons['buttons'][] = static::getFooterIconFor(Pie::ID);
421 }
422
423 if ($view->config->show_tag_cloud) {
424 $graphViewIcons['buttons'][] = static::getFooterIconFor(Cloud::ID);
425 }
426 }
427
428 return $graphViewIcons;
429 }
430
431 private static function unsetComparisonParams(&$params)
432 {
433 unset($params['compareDates']);
434 unset($params['comparePeriods']);
435 unset($params['compareSegments']);
436 unset($params['compare']);
437 }
438 }
439