From 0a0a0d2cdef310e98636ef1b68fc4f9d4108b235 Mon Sep 17 00:00:00 2001 From: diosmosis Date: Tue, 9 Dec 2014 17:01:25 -0800 Subject: Refs #2624, add INI config options that will force a new visit if campaign/website referrer information changes between actions. Includes new tracker hook shouldForceNewVisit that dimensions can use to force a new visit during tracking. Also includes light refactoring to referrer dimensions. Testless. --- config/global.ini.php | 10 +++++ core/Columns/Dimension.php | 22 ++++++++++ core/Tracker/Visit.php | 66 +++++++++++++++++++++++++----- plugins/Referrers/Columns/Base.php | 30 +++++++++++++- plugins/Referrers/Columns/Campaign.php | 51 +++++++++++++++++++++-- plugins/Referrers/Columns/Keyword.php | 5 +-- plugins/Referrers/Columns/ReferrerName.php | 5 +-- plugins/Referrers/Columns/ReferrerType.php | 5 +-- plugins/Referrers/Columns/ReferrerUrl.php | 5 +-- plugins/Referrers/Columns/Website.php | 41 ++++++++++++++++++- 10 files changed, 207 insertions(+), 33 deletions(-) diff --git a/config/global.ini.php b/config/global.ini.php index 2bab7b130d..b4fde4c358 100644 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -586,6 +586,16 @@ campaign_var_name = "pk_cpn,pk_campaign,piwik_campaign,utm_campaign,utm_source,u ; Includes by default the GA style campaign keyword parameter utm_term campaign_keyword_var_name = "pk_kwd,pk_keyword,piwik_kwd,utm_term" +; if set to 1, actions that contain different campaign information from the last action in the current visit will +; be treated as the start of a new visit. This will include situations when campaign information was absent before, +; but is present now (or vice versa). +tracker_create_new_visit_when_campaign_changes = 1 + +; if set to 1, actions that contain different website referrer information from the last action in the current visit +; will be treatedas the start of a new visit. This will include situations when website referrer information was +; absent before, but is present now (or vice versa). +tracker_create_new_visit_when_website_referrer_changes = 0 + ; maximum length of a Page Title or a Page URL recorded in the log_action.name table page_maximum_length = 1024; diff --git a/core/Columns/Dimension.php b/core/Columns/Dimension.php index e760a09083..0da3bcf8b3 100644 --- a/core/Columns/Dimension.php +++ b/core/Columns/Dimension.php @@ -15,6 +15,9 @@ use Piwik\Plugin\Dimension\ActionDimension; use Piwik\Plugin\Dimension\ConversionDimension; use Piwik\Plugin\Dimension\VisitDimension; use Piwik\Plugin\Segment; +use Piwik\Tracker\Action; +use Piwik\Tracker\Request; +use Piwik\Tracker\Visitor; use Piwik\Translate; /** @@ -166,6 +169,25 @@ abstract class Dimension return $pluginName . '.' . $dimensionName; } + /** + * This hook is executed by the tracker when determining if an action is the start of a new visit + * or part of an existing one. Derived classes can use it to force new visits based on dimension + * data. + * + * For example, the Campaign dimension in the Referrers plugin will force a new visit if the + * campaign information for the current action is different from the last. + * + * @param Request $request The current tracker request information. + * @param Visitor $visitor The information for the currently recognized visitor. + * @param Action|null $action The current action information (if any). + * @return bool Return true to force a visit, false if otherwise. + * @api + */ + public function shouldForceNewVisit(Request $request, Visitor $visitor, Action $action = null) + { + return false; + } + /** * Gets an instance of all available visit, action and conversion dimension. * @return Dimension[] diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php index 4065ebe15c..869434d4cd 100644 --- a/core/Tracker/Visit.php +++ b/core/Tracker/Visit.php @@ -152,11 +152,7 @@ class Visit implements VisitInterface $this->visitorInfo = $visitor->getVisitorInfo(); - $isLastActionInTheSameVisit = $this->isLastActionInTheSameVisit($visitor); - - if (!$isLastActionInTheSameVisit) { - Common::printDebug("Visitor detected, but last action was more than 30 minutes ago..."); - } + $isNewVisit = $this->isVisitNew($visitor, $action); // Known visit when: // ( - the visitor has the Piwik cookie with the idcookie ID used by Piwik to match the visitor @@ -165,9 +161,7 @@ class Visit implements VisitInterface // ) // AND // - the last page view for this visitor was less than 30 minutes ago @see isLastActionInTheSameVisit() - if ($visitor->isVisitorKnown() - && $isLastActionInTheSameVisit - ) { + if (!$isNewVisit) { $idReferrerActionUrl = $this->visitorInfo['visit_exit_idaction_url']; $idReferrerActionName = $this->visitorInfo['visit_exit_idaction_name']; @@ -203,9 +197,7 @@ class Visit implements VisitInterface // - the visitor has the Piwik cookie but the last action was performed more than 30 min ago @see isLastActionInTheSameVisit() // - the visitor doesn't have the Piwik cookie, and couldn't be matched in @see recognizeTheVisitor() // - the visitor does have the Piwik cookie but the idcookie and idvisit found in the cookie didn't match to any existing visit in the DB - if (!$visitor->isVisitorKnown() - || !$isLastActionInTheSameVisit - ) { + if ($isNewVisit) { $this->handleNewVisit($visitor, $action, $visitIsConverted); if (!is_null($action)) { $action->record($visitor, 0, 0); @@ -550,6 +542,23 @@ class Visit implements VisitInterface return $valuesToUpdate; } + private function triggerPredicateHookOnDimensions($dimensions, $hook, Visitor $visitor, Action $action = null, $returnFirstTrue = true) + { + $result = $returnFirstTrue ? false : array(); + foreach ($dimensions as $dimension) { + $value = $dimension->$hook($this->request, $visitor, $action); + + if ($returnFirstTrue) { + if ($value) { + return true; + } + } else { + $result[] = $value; + } + } + return $result; + } + protected function getAllVisitDimensions() { $dimensions = VisitDimension::getAllDimensions(); @@ -598,4 +607,39 @@ class Visit implements VisitInterface { return $this->getModel()->createVisit($visit); } + + /** + * Determines if the tracker if the current action should be treated as the start of a new visit or + * an action in an existing visit. + * + * @param Visitor $visitor The current visit/visitor information. + * @param Action|null $action The current action being tracked. + * @return bool + */ + private function isVisitNew(Visitor $visitor, Action $action = null) + { + $isLastActionInTheSameVisit = $this->isLastActionInTheSameVisit($visitor); + + if (!$isLastActionInTheSameVisit) { + Common::printDebug("Visitor detected, but last action was more than 30 minutes ago..."); + + return true; + } + + $shouldForceNewVisit = $this->triggerPredicateHookOnDimensions($this->getAllVisitDimensions(), 'shouldForceNewVisit', $visitor, $action); + if ($shouldForceNewVisit) { + return true; + } + + // if we should create a new visit when the referrer changes, check if referrer changed + if ($this->createNewVisitWhenWebsiteReferrerChanges + && $visitor->isReferrerInformationDifferent() + ) { + Common::printDebug("Existing visit detected, but creating new visit because referrer information is different than last action"); + + return true; + } + + return !$visitor->isVisitorKnown(); + } } diff --git a/plugins/Referrers/Columns/Base.php b/plugins/Referrers/Columns/Base.php index a69fcc54bf..a721dc9421 100644 --- a/plugins/Referrers/Columns/Base.php +++ b/plugins/Referrers/Columns/Base.php @@ -127,6 +127,14 @@ abstract class Base extends VisitDimension return $referrerInformation; } + protected function getReferrerInformationFromRequest(Request $request) + { + $referrerUrl = $request->getParam('urlref'); + $currentUrl = $request->getParam('url'); + + return $this->getReferrerInformation($referrerUrl, $currentUrl, $request->getIdSite()); + } + /** * Search engine detection * @return bool @@ -399,4 +407,24 @@ abstract class Base extends VisitDimension } } -} + protected function hasReferrerInformationChanged(Visitor $visitor, $information) + { + foreach (array('referer_keyword', 'referer_name', 'referer_type') as $infoName) { + if ($this->hasReferrerColumnChanged($visitor, $information, $infoName)) { + return true; + } + } + return false; + } + + protected function hasReferrerColumnChanged(Visitor $visitor, $information, $infoName) + { + return Common::mb_strtolower($visitor->getVisitorColumn($infoName)) != $information[$infoName]; + } + + protected function doesLastOrCurrentActionHaveSameReferrer(Visitor $visitor, $currentInformation, $referrerType) + { + return $visitor->getVisitorColumn('referer_type') == $referrerType + || $currentInformation['referer_type'] == $referrerType; + } +} \ No newline at end of file diff --git a/plugins/Referrers/Columns/Campaign.php b/plugins/Referrers/Columns/Campaign.php index 4413cd702f..53f86e97eb 100644 --- a/plugins/Referrers/Columns/Campaign.php +++ b/plugins/Referrers/Columns/Campaign.php @@ -8,13 +8,58 @@ */ namespace Piwik\Plugins\Referrers\Columns; -use Piwik\Columns\Dimension; +use Piwik\Common; use Piwik\Piwik; +use Piwik\Tracker\Action; +use Piwik\Tracker\Request; +use Piwik\Tracker\TrackerConfig; +use Piwik\Tracker\Visitor; -class Campaign extends Dimension +class Campaign extends Base { + /** + * Obtained from the `[Tracker] tracker_create_new_visit_when_campaign_changes` INI config option. + * If true, will create new visits when campaign name changes. + * + * @var bool + */ + protected $createNewVisitWhenCampaignChanges; + + public function __construct() + { + $this->createNewVisitWhenCampaignChanges = TrackerConfig::getConfigValue('tracker_create_new_visit_when_campaign_changes') == 1; + } + public function getName() { return Piwik::translate('Referrers_ColumnCampaign'); } -} + + /** + * If we should create a new visit when the campaign changes, check if the campaign info changed and if so + * force the tracker to create a new visit. + * + * @param Request $request + * @param Visitor $visitor + * @param Action|null $action + * @return bool + */ + public function shouldForceNewVisit(Request $request, Visitor $visitor, Action $action = null) + { + if (!$this->createNewVisitWhenCampaignChanges) { + return false; + } + + $information = $this->getReferrerInformationFromRequest($request); + + if ($this->doesLastOrCurrentActionHaveSameReferrer($visitor, $information, Common::REFERRER_TYPE_CAMPAIGN) + && $this->hasReferrerInformationChanged($visitor, $information) + ) { + Common::printDebug("Existing visit detected, but creating new visit because campaign information is different than last action."); + + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/plugins/Referrers/Columns/Keyword.php b/plugins/Referrers/Columns/Keyword.php index a5025b63c2..78605c9d94 100644 --- a/plugins/Referrers/Columns/Keyword.php +++ b/plugins/Referrers/Columns/Keyword.php @@ -41,10 +41,7 @@ class Keyword extends Base */ public function onNewVisit(Request $request, Visitor $visitor, $action) { - $referrerUrl = $request->getParam('urlref'); - $currentUrl = $request->getParam('url'); - - $information = $this->getReferrerInformation($referrerUrl, $currentUrl, $request->getIdSite()); + $information = $this->getReferrerInformationFromRequest($request); if (!empty($information['referer_keyword'])) { return substr($information['referer_keyword'], 0, 255); diff --git a/plugins/Referrers/Columns/ReferrerName.php b/plugins/Referrers/Columns/ReferrerName.php index fbd55219dc..7bdb0732a6 100644 --- a/plugins/Referrers/Columns/ReferrerName.php +++ b/plugins/Referrers/Columns/ReferrerName.php @@ -35,10 +35,7 @@ class ReferrerName extends Base */ public function onNewVisit(Request $request, Visitor $visitor, $action) { - $referrerUrl = $request->getParam('urlref'); - $currentUrl = $request->getParam('url'); - - $information = $this->getReferrerInformation($referrerUrl, $currentUrl, $request->getIdSite()); + $information = $this->getReferrerInformationFromRequest($request); if (!empty($information['referer_name'])) { diff --git a/plugins/Referrers/Columns/ReferrerType.php b/plugins/Referrers/Columns/ReferrerType.php index f4e688f60e..5303a1c360 100644 --- a/plugins/Referrers/Columns/ReferrerType.php +++ b/plugins/Referrers/Columns/ReferrerType.php @@ -42,10 +42,7 @@ class ReferrerType extends Base */ public function onNewVisit(Request $request, Visitor $visitor, $action) { - $referrerUrl = $request->getParam('urlref'); - $currentUrl = $request->getParam('url'); - - $information = $this->getReferrerInformation($referrerUrl, $currentUrl, $request->getIdSite()); + $information = $this->getReferrerInformationFromRequest($request); return $information['referer_type']; } diff --git a/plugins/Referrers/Columns/ReferrerUrl.php b/plugins/Referrers/Columns/ReferrerUrl.php index 0404a13247..21c5cd5bdc 100644 --- a/plugins/Referrers/Columns/ReferrerUrl.php +++ b/plugins/Referrers/Columns/ReferrerUrl.php @@ -35,10 +35,7 @@ class ReferrerUrl extends Base */ public function onNewVisit(Request $request, Visitor $visitor, $action) { - $referrerUrl = $request->getParam('urlref'); - $currentUrl = $request->getParam('url'); - - $information = $this->getReferrerInformation($referrerUrl, $currentUrl, $request->getIdSite()); + $information = $this->getReferrerInformationFromRequest($request); return $information['referer_url']; } diff --git a/plugins/Referrers/Columns/Website.php b/plugins/Referrers/Columns/Website.php index 53b143d693..7d4903c902 100644 --- a/plugins/Referrers/Columns/Website.php +++ b/plugins/Referrers/Columns/Website.php @@ -8,13 +8,50 @@ */ namespace Piwik\Plugins\Referrers\Columns; -use Piwik\Columns\Dimension; +use Piwik\Common; use Piwik\Piwik; +use Piwik\Tracker\Action; +use Piwik\Tracker\Request; +use Piwik\Tracker\TrackerConfig; +use Piwik\Tracker\Visitor; -class Website extends Dimension +class Website extends Base { + /** + * Set using the `[Tracker] tracker_create_new_visit_when_website_referrer_changes` INI config option. + * If true, will force new visits if the referrer website changes. + * + * @var bool + */ + protected $createNewVisitWhenWebsiteReferrerChanges; + + public function __construct() + { + $this->createNewVisitWhenWebsiteReferrerChanges = TrackerConfig::getConfigValue('tracker_create_new_visit_when_website_referrer_changes') == 1; + } + public function getName() { return Piwik::translate('General_Website'); } + + public function shouldForceNewVisit(Request $request, Visitor $visitor, Action $action = null) + { + if (!$this->createNewVisitWhenWebsiteReferrerChanges) { + return false; + } + + $information = $this->getReferrerInformationFromRequest($request); + + if ($this->doesLastOrCurrentActionHaveSameReferrer($visitor, $information, Common::REFERRER_TYPE_WEBSITE) + && $this->hasReferrerInformationChanged($visitor, $information) + ) { + Common::printDebug("Existing visit detected, but creating new visit because website referrer information is different than last action."); + + return true; + } + + return false; + + } } \ No newline at end of file -- cgit v1.2.3