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 |