PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.14.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.14.1
5.13.0 5.12.1 5.12.0 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 / plugins / Transitions / API.php
matomo / app / plugins / Transitions Last commit date
Categories 4 years ago Widgets 4 years ago config 6 years ago images 6 years ago lang 4 years ago stylesheets 5 years ago templates 4 years ago vue 4 years ago API.php 3 years ago Controller.php 4 years ago Transitions.php 4 years ago
API.php
726 lines
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 */
9
10 namespace Piwik\Plugins\Transitions;
11
12 use Exception;
13 use Piwik\ArchiveProcessor;
14 use Piwik\Common;
15 use Piwik\Config;
16 use Piwik\DataAccess\LogAggregator;
17 use Piwik\DataArray;
18 use Piwik\DataTable;
19 use Piwik\DataTable\Row;
20 use Piwik\Db;
21 use Piwik\Metrics;
22 use Piwik\Period;
23 use Piwik\Piwik;
24 use Piwik\Plugins\Actions\ArchivingHelper;
25 use Piwik\Plugins\Live\Model;
26 use Piwik\RankingQuery;
27 use Piwik\Segment;
28 use Piwik\Segment\SegmentExpression;
29 use Piwik\Site;
30 use Piwik\Tracker\Action;
31 use Piwik\Tracker\PageUrl;
32 use Piwik\Tracker\TableLogAction;
33
34 /**
35 * @method static \Piwik\Plugins\Transitions\API getInstance()
36 */
37 class API extends \Piwik\Plugin\API
38 {
39 public function getTransitionsForPageTitle($pageTitle, $idSite, $period, $date, $segment = false, $limitBeforeGrouping = 0)
40 {
41 return $this->getTransitionsForAction($pageTitle, 'title', $idSite, $period, $date, $segment, $limitBeforeGrouping);
42 }
43
44 public function getTransitionsForPageUrl($pageUrl, $idSite, $period, $date, $segment = false, $limitBeforeGrouping = 0)
45 {
46 return $this->getTransitionsForAction($pageUrl, 'url', $idSite, $period, $date, $segment, $limitBeforeGrouping);
47 }
48
49 /**
50 * General method to get transitions for an action
51 *
52 * @param $actionName
53 * @param $actionType "url"|"title"
54 * @param $idSite
55 * @param $period
56 * @param $date
57 * @param bool $segment
58 * @param int $limitBeforeGrouping
59 * @param string $parts
60 * @return array
61 * @throws Exception
62 */
63 public function getTransitionsForAction($actionName, $actionType, $idSite, $period, $date,
64 $segment = false, $limitBeforeGrouping = 0, $parts = 'all')
65 {
66 Piwik::checkUserHasViewAccess($idSite);
67
68 if (!$this->isPeriodAllowed($idSite, $period, $date)) {
69 throw new Exception('PeriodNotAllowed');
70 }
71
72 if ($limitBeforeGrouping && !is_numeric($limitBeforeGrouping)) {
73 throw new Exception('limitBeforeGrouping has to be an integer.');
74 }
75 //convert string to int
76 $limitBeforeGrouping = (int)$limitBeforeGrouping;
77
78 // get idaction of the requested action
79 $idaction = $this->deriveIdAction($actionName, $actionType);
80 if ($idaction < 0) {
81 throw new Exception('NoDataForAction');
82 }
83
84 // prepare log aggregator
85 $site = new Site($idSite);
86 $period = Period\Factory::build($period, $date);
87 $segment = new Segment(
88 $segment,
89 $idSite,
90 $period->getDateTimeStart()->setTimezone($site->getTimezone()),
91 $period->getDateTimeEnd()->setTimezone($site->getTimezone())
92 );
93 $params = new ArchiveProcessor\Parameters($site, $period, $segment);
94 $logAggregator = new LogAggregator($params);
95
96 // prepare the report
97 $report = array(
98 'date' => Period\Factory::build($period->getLabel(), $date)->getLocalizedShortString()
99 );
100
101 try {
102 $partsArray = explode(',', $parts);
103 if ($parts == 'all' || in_array('internalReferrers', $partsArray)) {
104 $this->addInternalReferrers($logAggregator, $report, $idaction, $actionType, $limitBeforeGrouping);
105 }
106 if ($parts == 'all' || in_array('followingActions', $partsArray)) {
107 $includeLoops = $parts != 'all' && !in_array('internalReferrers', $partsArray);
108 $this->addFollowingActions($logAggregator, $report, $idaction, $actionType, $limitBeforeGrouping, $includeLoops);
109 }
110 if ($parts == 'all' || in_array('externalReferrers', $partsArray)) {
111 $this->addExternalReferrers($logAggregator, $report, $idaction, $actionType, $limitBeforeGrouping);
112 }
113
114 // derive the number of exits from the other metrics
115 if ($parts == 'all') {
116 $report['pageMetrics']['exits'] = $report['pageMetrics']['pageviews']
117 - $this->getTotalTransitionsToFollowingActions()
118 - $report['pageMetrics']['loops'];
119 }
120 } catch (\Exception $e) {
121 Model::handleMaxExecutionTimeError(
122 Db::getReader(),
123 $e,
124 $segment->getString(),
125 $period->getDateStart(),
126 $period->getDateEnd(),
127 0,
128 Config::getInstance()->General['live_query_max_execution_time'],
129 ['method' => 'Transitions.getTransitionsForAction', 'actionName' => $actionName, 'actionType' => $actionType]
130 );
131 throw $e;
132 }
133
134
135 // replace column names in the data tables
136 $reportNames = array(
137 'previousPages' => true,
138 'previousSiteSearches' => false,
139 'followingPages' => true,
140 'followingSiteSearches' => false,
141 'outlinks' => true,
142 'downloads' => true
143 );
144 foreach ($reportNames as $reportName => $replaceLabel) {
145 if (isset($report[$reportName])) {
146 $columnNames = array(Metrics::INDEX_NB_ACTIONS => 'referrals');
147 if ($replaceLabel) {
148 $columnNames[Metrics::INDEX_NB_ACTIONS] = 'referrals';
149 }
150 $report[$reportName]->filter('ReplaceColumnNames', array($columnNames));
151 }
152 }
153
154 return $report;
155 }
156
157 /**
158 * Derive the action ID from the request action name and type.
159 */
160 private function deriveIdAction($actionName, $actionType)
161 {
162 switch ($actionType) {
163 case 'url':
164 $originalActionName = $actionName;
165 $actionName = Common::unsanitizeInputValue($actionName);
166 $id = TableLogAction::getIdActionFromSegment($actionName, 'idaction_url', SegmentExpression::MATCH_EQUAL, 'pageUrl');
167
168 if ($id < 0) {
169 // an example where this is needed is urls containing < or >
170 $actionName = $originalActionName;
171 $id = TableLogAction::getIdActionFromSegment($actionName, 'idaction_url', SegmentExpression::MATCH_EQUAL, 'pageUrl');
172 }
173
174 return $id;
175
176 case 'title':
177 $id = TableLogAction::getIdActionFromSegment($actionName, 'idaction_name', SegmentExpression::MATCH_EQUAL, 'pageTitle');
178
179 if ($id < 0) {
180 $unknown = ArchivingHelper::getUnknownActionName(Action::TYPE_PAGE_TITLE);
181 if (trim($actionName) == trim($unknown)) {
182 $id = TableLogAction::getIdActionFromSegment('', 'idaction_name', SegmentExpression::MATCH_EQUAL, 'pageTitle');
183 }
184 }
185
186 return $id;
187
188 default:
189 throw new Exception('Unknown action type');
190 }
191 }
192
193 /**
194 * Add the internal referrers to the report:
195 * previous pages and previous site searches
196 *
197 * @param LogAggregator $logAggregator
198 * @param $report
199 * @param $idaction
200 * @param string $actionType
201 * @param $limitBeforeGrouping
202 * @throws Exception
203 */
204 private function addInternalReferrers($logAggregator, &$report, $idaction, $actionType, $limitBeforeGrouping)
205 {
206 $data = $this->queryInternalReferrers($idaction, $actionType, $logAggregator, $limitBeforeGrouping);
207
208 if ($data['pageviews'] == 0) {
209 throw new Exception('NoDataForAction');
210 }
211
212 $report['previousPages'] = & $data['previousPages'];
213 $report['previousSiteSearches'] = & $data['previousSiteSearches'];
214 $report['pageMetrics']['loops'] = $data['loops'];
215 $report['pageMetrics']['pageviews'] = $data['pageviews'];
216 }
217
218 /**
219 * Add the following actions to the report:
220 * following pages, downloads, outlinks
221 *
222 * @param LogAggregator $logAggregator
223 * @param $report
224 * @param $idaction
225 * @param string $actionType
226 * @param int $limitBeforeGrouping
227 * @param boolean $includeLoops
228 */
229 private function addFollowingActions($logAggregator, &$report, $idaction, $actionType, $limitBeforeGrouping = 0, $includeLoops = false)
230 {
231
232 $data = $this->queryFollowingActions(
233 $idaction, $actionType, $logAggregator, $limitBeforeGrouping, $includeLoops);
234
235 foreach ($data as $tableName => $table) {
236 $report[$tableName] = $table;
237 }
238 }
239
240 /**
241 * Get information about the following actions (following pages, site searches, outlinks, downloads)
242 *
243 * @param $idaction
244 * @param $actionType
245 * @param LogAggregator $logAggregator
246 * @param $limitBeforeGrouping
247 * @param $includeLoops
248 * @return array(followingPages:DataTable, outlinks:DataTable, downloads:DataTable)
249 */
250 protected function queryFollowingActions($idaction, $actionType, LogAggregator $logAggregator,
251 $limitBeforeGrouping = 0, $includeLoops = false)
252 {
253 $types = array();
254
255 if ($actionType != 'title') {
256 // specific setup for page urls
257 $types[Action::TYPE_PAGE_URL] = 'followingPages';
258 $dimension = 'if ( %1$s.idaction_url IS NULL, %1$s.idaction_name, %1$s.idaction_url )';
259 $dimension = sprintf($dimension, 'log_link_visit_action' );
260 // site search referrers are logged with url=NULL
261 // when we find one, we have to join on name
262 $joinLogActionColumn = $dimension;
263 $selects = array('log_action.name', 'log_action.url_prefix', 'log_action.type');
264 } else {
265 // specific setup for page titles:
266 $types[Action::TYPE_PAGE_TITLE] = 'followingPages';
267 // join log_action on name and url and pick depending on url type
268 // the table joined on url is log_action1
269 $joinLogActionColumn = array('idaction_url', 'idaction_name');
270 $dimension = '
271 CASE
272 ' /* following site search */ . '
273 WHEN log_link_visit_action.idaction_url IS NULL THEN log_action2.idaction
274 ' /* following page view: use page title */ . '
275 WHEN log_action1.type = ' . Action::TYPE_PAGE_URL . ' THEN log_action2.idaction
276 ' /* following download or outlink: use url */ . '
277 ELSE log_action1.idaction
278 END
279 ';
280 $selects = array(
281 'CASE
282 ' /* following site search */ . '
283 WHEN log_link_visit_action.idaction_url IS NULL THEN log_action2.name
284 ' /* following page view: use page title */ . '
285 WHEN log_action1.type = ' . Action::TYPE_PAGE_URL . ' THEN log_action2.name
286 ' /* following download or outlink: use url */ . '
287 ELSE log_action1.name
288 END AS `name`',
289 'CASE
290 ' /* following site search */ . '
291 WHEN log_link_visit_action.idaction_url IS NULL THEN log_action2.type
292 ' /* following page view: use page title */ . '
293 WHEN log_action1.type = ' . Action::TYPE_PAGE_URL . ' THEN log_action2.type
294 ' /* following download or outlink: use url */ . '
295 ELSE log_action1.type
296 END AS `type`',
297 'NULL AS `url_prefix`'
298 );
299 }
300
301 // these types are available for both titles and urls
302 $types[Action::TYPE_SITE_SEARCH] = 'followingSiteSearches';
303 $types[Action::TYPE_OUTLINK] = 'outlinks';
304 $types[Action::TYPE_DOWNLOAD] = 'downloads';
305
306 $rankingQuery = new RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping: $this->limitBeforeGrouping);
307 $rankingQuery->setOthersLabel('Others');
308 $rankingQuery->addLabelColumn(array('name', 'url_prefix'));
309 $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($types));
310
311 $type = $this->getColumnTypeSuffix($actionType);
312 $where = 'log_link_visit_action.idaction_' . $type . '_ref = ' . intval($idaction);
313 if (!$includeLoops) {
314 $where .= ' AND (log_link_visit_action.idaction_' . $type . ' IS NULL OR '
315 . 'log_link_visit_action.idaction_' . $type . ' != ' . intval($idaction) . ')';
316 }
317
318 $metrics = array(Metrics::INDEX_NB_ACTIONS);
319 $data = $logAggregator->queryActionsByDimension(
320 array($dimension),
321 $where,
322 $selects,
323 $metrics,
324 $rankingQuery,
325 $joinLogActionColumn,
326 $secondaryOrderBy = "`name`",
327 Config::getInstance()->General['live_query_max_execution_time']
328 );
329
330 $dataTables = $this->makeDataTablesFollowingActions($types, $data);
331
332 return $dataTables;
333 }
334
335 /**
336 * Get information about external referrers (i.e. search engines, websites & campaigns)
337 *
338 * @param $idaction
339 * @param $actionType
340 * @param LogAggregator $logAggregator
341 * @param int $limitBeforeGrouping
342 * @return DataTable
343 * @throws Exception
344 */
345 protected function queryExternalReferrers($idaction, $actionType, $logAggregator, $limitBeforeGrouping = 0)
346 {
347
348 $rankingQuery = new RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping: $this->limitBeforeGrouping);
349 $rankingQuery->setOthersLabel('Others');
350
351 // we generate a single column that contains the interesting data for each referrer.
352 // the reason we cannot group by referer_* becomes clear when we look at search engine keywords.
353 // referer_url contains the url from the search engine, referer_keyword the keyword we want to
354 // group by. when we group by both, we don't get a single column for the keyword but instead
355 // one column per keyword + search engine url. this way, we could not get the top keywords using
356 // the ranking query.
357 $dimensions = array('referrer_data', 'referer_type');
358 $rankingQuery->addLabelColumn('referrer_data');
359 $selects = array(
360 'CASE log_visit.referer_type
361 WHEN ' . Common::REFERRER_TYPE_DIRECT_ENTRY . ' THEN \'\'
362 WHEN ' . Common::REFERRER_TYPE_SEARCH_ENGINE . ' THEN log_visit.referer_keyword
363 WHEN ' . Common::REFERRER_TYPE_SOCIAL_NETWORK . ' THEN log_visit.referer_name
364 WHEN ' . Common::REFERRER_TYPE_WEBSITE . ' THEN log_visit.referer_url
365 WHEN ' . Common::REFERRER_TYPE_CAMPAIGN . ' THEN CONCAT(log_visit.referer_name, \' \', log_visit.referer_keyword)
366 END AS `referrer_data`');
367
368 // get one limited group per referrer type
369 $rankingQuery->partitionResultIntoMultipleGroups('referer_type', array(
370 Common::REFERRER_TYPE_DIRECT_ENTRY,
371 Common::REFERRER_TYPE_SEARCH_ENGINE,
372 Common::REFERRER_TYPE_SOCIAL_NETWORK,
373 Common::REFERRER_TYPE_WEBSITE,
374 Common::REFERRER_TYPE_CAMPAIGN
375 ));
376
377 $type = $this->getColumnTypeSuffix($actionType);
378 $where = 'visit_entry_idaction_' . $type . ' = ' . intval($idaction);
379
380 $metrics = array(Metrics::INDEX_NB_VISITS);
381 $data = $logAggregator->queryVisitsByDimension($dimensions, $where, $selects, $metrics, $rankingQuery, false, Config::getInstance()->General['live_query_max_execution_time']);
382
383 $referrerData = array();
384 $referrerSubData = array();
385
386 foreach ($data as $referrerType => &$subData) {
387 $referrerData[$referrerType] = array(Metrics::INDEX_NB_VISITS => 0);
388 if ($referrerType != Common::REFERRER_TYPE_DIRECT_ENTRY) {
389 $referrerSubData[$referrerType] = array();
390 }
391
392 foreach ($subData as &$row) {
393 if ($referrerType == Common::REFERRER_TYPE_SEARCH_ENGINE && empty($row['referrer_data'])) {
394 $row['referrer_data'] = Piwik::translate('General_Unknown');
395 }
396
397 $referrerData[$referrerType][Metrics::INDEX_NB_VISITS] += $row[Metrics::INDEX_NB_VISITS];
398
399 $label = $row['referrer_data'];
400 if ($label) {
401 $referrerSubData[$referrerType][$label] = array(
402 Metrics::INDEX_NB_VISITS => $row[Metrics::INDEX_NB_VISITS]
403 );
404 }
405 }
406 }
407
408 $array = new DataArray($referrerData, $referrerSubData);
409 return $array->asDataTable();
410 }
411
412 /**
413 * Get information about internal referrers (previous pages & loops, i.e. page refreshes)
414 *
415 * @param $idaction
416 * @param $actionType
417 * @param LogAggregator $logAggregator
418 * @param int $limitBeforeGrouping
419 * @return array(previousPages:DataTable, loops:integer)
420 */
421 protected function queryInternalReferrers($idaction, $actionType, $logAggregator, $limitBeforeGrouping = 0)
422 {
423 $keyIsOther = 0;
424 $keyIsPageUrlAction = 1;
425 $keyIsSiteSearchAction = 2;
426
427 $rankingQuery = new RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping: $this->limitBeforeGrouping);
428 $rankingQuery->setOthersLabel('Others');
429 $rankingQuery->addLabelColumn(array('name', 'url_prefix'));
430 $rankingQuery->setColumnToMarkExcludedRows('is_self');
431 $rankingQuery->partitionResultIntoMultipleGroups('action_partition', array($keyIsOther, $keyIsPageUrlAction, $keyIsSiteSearchAction));
432
433 $type = $this->getColumnTypeSuffix($actionType);
434 $mainActionType = Action::TYPE_PAGE_URL;
435 $dimension = 'idaction_url_ref';
436
437 if ($actionType == 'title') {
438 $mainActionType = Action::TYPE_PAGE_TITLE;
439 $dimension = 'idaction_name_ref';
440 }
441
442 $selects = array(
443 'log_action.name',
444 'log_action.url_prefix',
445 'CASE WHEN log_link_visit_action.idaction_' . $type . '_ref = ' . intval($idaction) . ' THEN 1 ELSE 0 END AS `is_self`',
446 'CASE
447 WHEN log_action.type = ' . $mainActionType . ' THEN ' . $keyIsPageUrlAction . '
448 WHEN log_action.type = ' . Action::TYPE_SITE_SEARCH . ' THEN ' . $keyIsSiteSearchAction .'
449 ELSE ' . $keyIsOther . '
450 END AS `action_partition`'
451 );
452
453 $where = ' log_link_visit_action.idaction_' . $type . ' = ' . intval($idaction);
454
455 if ($dimension == 'idaction_url_ref') {
456 // site search referrers are logged with url_ref=NULL
457 // when we find one, we have to join on name_ref
458 $dimension = 'if ( %1$s.idaction_url_ref IS NULL, %1$s.idaction_name_ref, %1$s.idaction_url_ref )';
459 $dimension = sprintf($dimension, 'log_link_visit_action');
460 $joinLogActionOn = $dimension;
461 } else {
462 $joinLogActionOn = $dimension;
463 }
464 $metrics = array(Metrics::INDEX_NB_ACTIONS);
465 $data = $logAggregator->queryActionsByDimension(
466 array($dimension),
467 $where,
468 $selects,
469 $metrics,
470 $rankingQuery,
471 $joinLogActionOn,
472 $secondaryOrderBy = "`name`",
473 Config::getInstance()->General['live_query_max_execution_time']
474 );
475
476 $loops = 0;
477 $nbPageviews = 0;
478 $previousPagesDataTable = new DataTable;
479 if (isset($data['result'][$keyIsPageUrlAction])) {
480 foreach ($data['result'][$keyIsPageUrlAction] as &$page) {
481 $nbActions = intval($page[Metrics::INDEX_NB_ACTIONS]);
482 $previousPagesDataTable->addRow(new Row(array(
483 Row::COLUMNS => array(
484 'label' => $this->getPageLabel($page, Action::TYPE_PAGE_URL),
485 Metrics::INDEX_NB_ACTIONS => $nbActions
486 )
487 )));
488 $nbPageviews += $nbActions;
489 }
490 }
491
492 $previousSearchesDataTable = new DataTable;
493 if (isset($data['result'][$keyIsSiteSearchAction])) {
494 foreach ($data['result'][$keyIsSiteSearchAction] as &$search) {
495 $nbActions = intval($search[Metrics::INDEX_NB_ACTIONS]);
496 $previousSearchesDataTable->addRow(new Row(array(
497 Row::COLUMNS => array(
498 'label' => $search['name'],
499 Metrics::INDEX_NB_ACTIONS => $nbActions
500 )
501 )));
502 $nbPageviews += $nbActions;
503 }
504 }
505
506 if (isset($data['result'][0])) {
507 foreach ($data['result'][0] as &$referrer) {
508 $nbPageviews += intval($referrer[Metrics::INDEX_NB_ACTIONS]);
509 }
510 }
511
512 if (count($data['excludedFromLimit'])) {
513 $loops += intval($data['excludedFromLimit'][0][Metrics::INDEX_NB_ACTIONS]);
514 $nbPageviews += $loops;
515 }
516
517 return array(
518 'pageviews' => $nbPageviews,
519 'previousPages' => $previousPagesDataTable,
520 'previousSiteSearches' => $previousSearchesDataTable,
521 'loops' => $loops
522 );
523 }
524
525 private function getPageLabel(&$pageRecord, $type)
526 {
527 if ($type == Action::TYPE_PAGE_TITLE) {
528 $label = $pageRecord['name'];
529 if (empty($label)) {
530 $label = ArchivingHelper::getUnknownActionName(Action::TYPE_PAGE_TITLE);
531 }
532 return $label;
533 }
534
535 if ($type == Action::TYPE_OUTLINK || $type == Action::TYPE_DOWNLOAD) {
536 return PageUrl::reconstructNormalizedUrl($pageRecord['name'], $pageRecord['url_prefix']);
537 }
538
539 return $pageRecord['name'];
540 }
541
542 private function getColumnTypeSuffix($actionType)
543 {
544 if ($actionType == 'title') {
545 return 'name';
546 }
547 return 'url';
548 }
549
550 private $limitBeforeGrouping = 5;
551 private $totalTransitionsToFollowingPages = 0;
552
553 /**
554 * Get the sum of all transitions to following actions (pages, outlinks, downloads).
555 * Only works if queryFollowingActions() has been used directly before.
556 */
557 protected function getTotalTransitionsToFollowingActions()
558 {
559 return $this->totalTransitionsToFollowingPages;
560 }
561
562 /**
563 * Add the external referrers to the report:
564 * direct entries, websites, campaigns, search engines
565 *
566 * @param LogAggregator $logAggregator
567 * @param $report
568 * @param $idaction
569 * @param string $actionType
570 * @param $limitBeforeGrouping
571 */
572 private function addExternalReferrers($logAggregator, &$report, $idaction, $actionType, $limitBeforeGrouping)
573 {
574 $data = $this->queryExternalReferrers(
575 $idaction, $actionType, $logAggregator, $limitBeforeGrouping);
576
577 $report['pageMetrics']['entries'] = 0;
578 $report['referrers'] = array();
579 foreach ($data->getRows() as $row) {
580 $referrerId = $row->getColumn('label');
581 $visits = $row->getColumn(Metrics::INDEX_NB_VISITS);
582 if ($visits) {
583 // load details (i.e. subtables)
584 $details = array();
585 $subTable = $row->getSubtable();
586 if ($subTable) {
587 foreach ($subTable->getRows() as $subRow) {
588 $details[] = array(
589 'label' => $subRow->getColumn('label'),
590 'referrals' => $subRow->getColumn(Metrics::INDEX_NB_VISITS)
591 );
592 }
593 }
594 $report['referrers'][] = array(
595 'label' => $this->getReferrerLabel($referrerId),
596 'shortName' => \Piwik\Plugins\Referrers\getReferrerTypeFromShortName($referrerId),
597 'visits' => $visits,
598 'details' => $details
599 );
600 $report['pageMetrics']['entries'] += $visits;
601 }
602 }
603
604 // if there's no data for referrers, ResponseBuilder::handleMultiDimensionalArray
605 // does not detect the multi dimensional array and the data is rendered differently, which
606 // causes an exception.
607 if (count($report['referrers']) == 0) {
608 $report['referrers'][] = array(
609 'label' => $this->getReferrerLabel(Common::REFERRER_TYPE_DIRECT_ENTRY),
610 'shortName' => \Piwik\Plugins\Referrers\getReferrerTypeLabel(Common::REFERRER_TYPE_DIRECT_ENTRY),
611 'visits' => 0
612 );
613 }
614 }
615
616 private function getReferrerLabel($referrerId)
617 {
618 switch ($referrerId) {
619 case Common::REFERRER_TYPE_DIRECT_ENTRY:
620 return Controller::getTranslation('directEntries');
621 case Common::REFERRER_TYPE_SEARCH_ENGINE:
622 return Controller::getTranslation('fromSearchEngines');
623 case Common::REFERRER_TYPE_SOCIAL_NETWORK:
624 return Controller::getTranslation('fromSocialNetworks');
625 case Common::REFERRER_TYPE_WEBSITE:
626 return Controller::getTranslation('fromWebsites');
627 case Common::REFERRER_TYPE_CAMPAIGN:
628 return Controller::getTranslation('fromCampaigns');
629 default:
630 return Piwik::translate('General_Others');
631 }
632 }
633
634 public function getTranslations()
635 {
636 $controller = new Controller();
637 return $controller->getTranslations();
638 }
639
640 protected function makeDataTablesFollowingActions($types, $data)
641 {
642 $this->totalTransitionsToFollowingPages = 0;
643 $dataTables = array();
644 foreach ($types as $type => $recordName) {
645 $dataTable = new DataTable;
646 if (isset($data[$type])) {
647 foreach ($data[$type] as &$record) {
648 $actions = intval($record[Metrics::INDEX_NB_ACTIONS]);
649 $dataTable->addRow(new Row(array(
650 Row::COLUMNS => array(
651 'label' => $this->getPageLabel($record, $type),
652 Metrics::INDEX_NB_ACTIONS => $actions
653 )
654 )));
655
656 $this->processTransitionsToFollowingPages($type, $actions);
657 }
658 }
659 $dataTables[$recordName] = $dataTable;
660 }
661 return $dataTables;
662 }
663
664 protected function processTransitionsToFollowingPages($type, $actions)
665 {
666 // Downloads and Outlinks are not included as these actions count towards a Visit Exit
667 $actionTypesNotExitActions = array(
668 Action::TYPE_SITE_SEARCH,
669 Action::TYPE_PAGE_TITLE,
670 Action::TYPE_PAGE_URL
671 );
672 if(in_array($type, $actionTypesNotExitActions)) {
673 $this->totalTransitionsToFollowingPages += $actions;
674 }
675 }
676
677
678 /**
679 * Check if a period is allowed by config settings
680 *
681 * @param $idSite
682 * @param $period
683 * @param $date
684 *
685 * @return bool
686 */
687 public function isPeriodAllowed($idSite, $period, $date) : bool
688 {
689 $maxPeriodAllowed = Transitions::getPeriodAllowedConfig($idSite);
690 if ($maxPeriodAllowed === 'all') {
691 return true;
692 }
693
694 // If the period is a range then the number of days in the range must be less or equal to the max period allowed
695 if ($period === 'range') {
696
697 $range = new Period\Range($period, $date);
698 $rangeDays = $range->getDayCount();
699
700 switch ($maxPeriodAllowed) {
701 case 'day':
702 return $rangeDays == 1;
703 case 'week':
704 return $rangeDays <= 7;
705 case 'month':
706 return $rangeDays <= 31;
707 case 'year':
708 return $rangeDays <= 365;
709 }
710 }
711
712 switch ($maxPeriodAllowed) {
713 case 'day':
714 return $period === 'day';
715 case 'week':
716 return in_array($period, ['day', 'week']);
717 case 'month':
718 return in_array($period, ['day', 'week', 'month']);
719 case 'year':
720 return in_array($period, ['day', 'week', 'month', 'year']);
721 }
722
723 return false;
724 }
725 }
726