Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix new visit created when no referrer keyword is set + fix referrer problems with bulk tracking #9363

Merged
merged 1 commit into from Dec 21, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion core/Tracker/Visitor.php
Expand Up @@ -9,7 +9,6 @@
namespace Piwik\Tracker;

use Piwik\Config;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker;
use Piwik\Tracker\Visit\VisitProperties;

Expand Down Expand Up @@ -49,6 +48,11 @@ public function isVisitorKnown()
return $this->visitorKnown === true;
}

public function isNewVisit()
{
return !$this->isVisitorKnown();
}

private function setIsVisitorKnown($isVisitorKnown)
{
return $this->visitorKnown = $isVisitorKnown;
Expand Down
97 changes: 57 additions & 40 deletions plugins/Referrers/Columns/Base.php
Expand Up @@ -34,12 +34,12 @@ abstract class Base extends VisitDimension
protected $currentUrlParse;
protected $idsite;

private static $cachedReferrerSearchEngine = array();

// Used to prefix when a adsense referrer is detected
const LABEL_PREFIX_ADWORDS_KEYWORD = '(adwords) ';
const LABEL_ADWORDS_NAME = 'AdWords';

private static $cachedReferrer = array();

/**
* Returns an array containing the following information:
* - referer_type
Expand Down Expand Up @@ -69,14 +69,8 @@ abstract class Base extends VisitDimension
* @param int $idSite
* @return array
*/
protected function getReferrerInformation($referrerUrl, $currentUrl, $idSite, Request $request)
protected function getReferrerInformation($referrerUrl, $currentUrl, $idSite, Request $request, Visitor $visitor)
{
$cacheKey = $referrerUrl . $currentUrl . $idSite;

if (isset(self::$cachedReferrer[$cacheKey])) {
return self::$cachedReferrer[$cacheKey];
}

$this->idsite = $idSite;

// default values for the referer_* fields
Expand All @@ -101,7 +95,7 @@ protected function getReferrerInformation($referrerUrl, $currentUrl, $idSite, Re
$this->referrerHost = $this->referrerUrlParse['host'];
}

$referrerDetected = $this->detectReferrerCampaign($request);
$referrerDetected = $this->detectReferrerCampaign($request, $visitor);

if (!$referrerDetected) {
if ($this->detectReferrerDirectEntry()
Expand Down Expand Up @@ -131,17 +125,15 @@ protected function getReferrerInformation($referrerUrl, $currentUrl, $idSite, Re
'referer_url' => $this->referrerUrl,
);

self::$cachedReferrer[$cacheKey] = $referrerInformation;

return $referrerInformation;
}

protected function getReferrerInformationFromRequest(Request $request)
protected function getReferrerInformationFromRequest(Request $request, Visitor $visitor)
{
$referrerUrl = $request->getParam('urlref');
$currentUrl = $request->getParam('url');

return $this->getReferrerInformation($referrerUrl, $currentUrl, $request->getIdSite(), $request);
return $this->getReferrerInformation($referrerUrl, $currentUrl, $request->getIdSite(), $request, $visitor);
}

/**
Expand All @@ -150,28 +142,36 @@ protected function getReferrerInformationFromRequest(Request $request)
*/
protected function detectReferrerSearchEngine()
{
$searchEngineInformation = SearchEngineDetection::getInstance()->extractInformationFromUrl($this->referrerUrl);

/**
* Triggered when detecting the search engine of a referrer URL.
*
* Plugins can use this event to provide custom search engine detection
* logic.
*
* @param array &$searchEngineInformation An array with the following information:
*
* - **name**: The search engine name.
* - **keywords**: The search keywords used.
*
* This parameter is initialized to the results
* of Piwik's default search engine detection
* logic.
* @param string referrerUrl The referrer URL from the tracking request.
*/
Piwik::postEvent('Tracker.detectReferrerSearchEngine', array(&$searchEngineInformation, $this->referrerUrl));
if (isset(self::$cachedReferrerSearchEngine[$this->referrerUrl])) {
$searchEngineInformation = self::$cachedReferrerSearchEngine[$this->referrerUrl];
} else {
$searchEngineInformation = SearchEngineDetection::getInstance()->extractInformationFromUrl($this->referrerUrl);

/**
* Triggered when detecting the search engine of a referrer URL.
*
* Plugins can use this event to provide custom search engine detection
* logic.
*
* @param array &$searchEngineInformation An array with the following information:
*
* - **name**: The search engine name.
* - **keywords**: The search keywords used.
*
* This parameter is initialized to the results
* of Piwik's default search engine detection
* logic.
* @param string referrerUrl The referrer URL from the tracking request.
*/
Piwik::postEvent('Tracker.detectReferrerSearchEngine', array(&$searchEngineInformation, $this->referrerUrl));

self::$cachedReferrerSearchEngine[$this->referrerUrl] = $searchEngineInformation;
}

if ($searchEngineInformation === false) {
return false;
}

$this->typeReferrerAnalyzed = Common::REFERRER_TYPE_SEARCH_ENGINE;
$this->nameReferrerAnalyzed = $searchEngineInformation['name'];
$this->keywordReferrerAnalyzed = $searchEngineInformation['keywords'];
Expand Down Expand Up @@ -354,7 +354,7 @@ protected function getParameterValueFromReferrerUrl($adsenseReferrerParameter)
/**
* @return bool
*/
protected function detectReferrerCampaign(Request $request)
protected function detectReferrerCampaign(Request $request, Visitor $visitor)
{
$isCampaign = $this->detectReferrerCampaignFromTrackerParams($request);
if (!$isCampaign) {
Expand All @@ -363,12 +363,30 @@ protected function detectReferrerCampaign(Request $request)

$this->detectCampaignKeywordFromReferrerUrl();

if ($this->typeReferrerAnalyzed != Common::REFERRER_TYPE_CAMPAIGN) {
return false;
}
$isCurrentVisitACampaignWithSameName = $visitor->getVisitorColumn('referer_name') == $this->nameReferrerAnalyzed;
$isCurrentVisitACampaignWithSameName = $isCurrentVisitACampaignWithSameName && $visitor->getVisitorColumn('referer_type') == Common::REFERRER_TYPE_CAMPAIGN;

// if we detected a campaign but there is still no keyword set, we set the keyword to the Referrer host
if (empty($this->keywordReferrerAnalyzed)) {
$this->keywordReferrerAnalyzed = $this->referrerHost;
if ($isCurrentVisitACampaignWithSameName) {
$this->keywordReferrerAnalyzed = $visitor->getVisitorColumn('referer_keyword');
// it is an existing visit and no referrer keyword was used initially (or a different host),
// we do not use the default referrer host in this case as it would create a new visit. It would create
// a new visit because initially the referrer keyword was not set (or from a different host) and now
// we would set it suddenly. The changed keyword would be recognized as a campaign change and a new
// visit would be forced. Why would it suddenly set a keyword but not do it initially?
// This happens when on the first visit when the URL was opened directly (no referrer or different host)
// and then the user navigates to another page where the referrer host becomes the own host
// (referrer = own website) see https://github.com/piwik/piwik/issues/9299
} else {
$this->keywordReferrerAnalyzed = $this->referrerHost;
}
}

if ($this->typeReferrerAnalyzed != Common::REFERRER_TYPE_CAMPAIGN) {
$this->keywordReferrerAnalyzed = null;
$this->nameReferrerAnalyzed = null;
return false;
}

$this->keywordReferrerAnalyzed = Common::mb_strtolower($this->keywordReferrerAnalyzed);
Expand All @@ -384,7 +402,6 @@ protected function detectReferrerCampaign(Request $request)
*/
public function getValueForRecordGoal(Request $request, Visitor $visitor)
{
$referrerTimestamp = $request->getParam('_refts');
$referrerUrl = $request->getParam('_ref');
$referrerCampaignName = $this->getReferrerCampaignQueryParam($request, '_rcn');
$referrerCampaignKeyword = $this->getReferrerCampaignQueryParam($request, '_rck');
Expand Down Expand Up @@ -422,7 +439,7 @@ public function getValueForRecordGoal(Request $request, Visitor $visitor)
elseif (!empty($referrerUrl)) {

$idSite = $request->getIdSite();
$referrer = $this->getReferrerInformation($referrerUrl, $currentUrl = '', $idSite, $request);
$referrer = $this->getReferrerInformation($referrerUrl, $currentUrl = '', $idSite, $request, $visitor);

// if the parsed referrer is interesting enough, ie. website or search engine
if (in_array($referrer['referer_type'], array(Common::REFERRER_TYPE_SEARCH_ENGINE, Common::REFERRER_TYPE_WEBSITE))) {
Expand Down
2 changes: 1 addition & 1 deletion plugins/Referrers/Columns/Campaign.php
Expand Up @@ -50,7 +50,7 @@ public function shouldForceNewVisit(Request $request, Visitor $visitor, Action $
return false;
}

$information = $this->getReferrerInformationFromRequest($request);
$information = $this->getReferrerInformationFromRequest($request, $visitor);

if ($information['referer_type'] == Common::REFERRER_TYPE_CAMPAIGN
&& $this->isReferrerInformationNew($visitor, $information)
Expand Down
2 changes: 1 addition & 1 deletion plugins/Referrers/Columns/Keyword.php
Expand Up @@ -42,7 +42,7 @@ public function getName()
*/
public function onNewVisit(Request $request, Visitor $visitor, $action)
{
$information = $this->getReferrerInformationFromRequest($request);
$information = $this->getReferrerInformationFromRequest($request, $visitor);

if (!empty($information['referer_keyword'])) {
return Common::mb_substr($information['referer_keyword'], 0, 255);
Expand Down
2 changes: 1 addition & 1 deletion plugins/Referrers/Columns/ReferrerName.php
Expand Up @@ -36,7 +36,7 @@ protected function configureSegments()
*/
public function onNewVisit(Request $request, Visitor $visitor, $action)
{
$information = $this->getReferrerInformationFromRequest($request);
$information = $this->getReferrerInformationFromRequest($request, $visitor);

if (!empty($information['referer_name'])) {
return Common::mb_substr($information['referer_name'], 0, 70);
Expand Down
2 changes: 1 addition & 1 deletion plugins/Referrers/Columns/ReferrerType.php
Expand Up @@ -42,7 +42,7 @@ public function getName()
*/
public function onNewVisit(Request $request, Visitor $visitor, $action)
{
$information = $this->getReferrerInformationFromRequest($request);
$information = $this->getReferrerInformationFromRequest($request, $visitor);

return $information['referer_type'];
}
Expand Down
2 changes: 1 addition & 1 deletion plugins/Referrers/Columns/ReferrerUrl.php
Expand Up @@ -35,7 +35,7 @@ protected function configureSegments()
*/
public function onNewVisit(Request $request, Visitor $visitor, $action)
{
$information = $this->getReferrerInformationFromRequest($request);
$information = $this->getReferrerInformationFromRequest($request, $visitor);

return $information['referer_url'];
}
Expand Down
2 changes: 1 addition & 1 deletion plugins/Referrers/Columns/Website.php
Expand Up @@ -41,7 +41,7 @@ public function shouldForceNewVisit(Request $request, Visitor $visitor, Action $
return false;
}

$information = $this->getReferrerInformationFromRequest($request);
$information = $this->getReferrerInformationFromRequest($request, $visitor);

if ($information['referer_type'] == Common::REFERRER_TYPE_WEBSITE
&& $this->isReferrerInformationNew($visitor, $information)
Expand Down
100 changes: 100 additions & 0 deletions plugins/Referrers/tests/Integration/Columns/ReferrerKeywordTest.php
@@ -0,0 +1,100 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/

namespace Piwik\Plugins\Referrers\tests\Integration\Columns;

use Piwik\Plugins\Referrers\Columns\Keyword;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visit\VisitProperties;
use Piwik\Tracker\Visitor;

/**
* @group Referrers
* @group ReferrerTypeTest
* @group ReferrerType
* @group Plugins
*/
class ReferrerKeywordTest extends IntegrationTestCase
{
/**
* @var Keyword
*/
private $keyword;
private $idSite1 = 1;
private $idSite2 = 2;

public function setUp()
{
parent::setUp();

$date = '2012-01-01 00:00:00';
$ecommerce = false;

Fixture::createWebsite($date, $ecommerce, $name = 'test1', $url = 'http://piwik.org/');
Fixture::createWebsite($date, $ecommerce, $name = 'test3', $url = 'http://piwik.pro/');

$this->keyword = new Keyword();
}

/**
* @dataProvider getReferrerUrls
*/
public function test_onNewVisit_shouldDetectCorrectReferrerType($expectedType, $idSite, $url, $referrerUrl)
{
$request = $this->getRequest(array('idsite' => $idSite, 'url' => $url, 'urlref' => $referrerUrl));
$type = $this->keyword->onNewVisit($request, $this->getNewVisitor(), $action = null);

$this->assertSame($expectedType, $type);
}

public function getReferrerUrls()
{
$url = 'http://piwik.org/foo/bar';
$noReferrer = '';
$directReferrer = 'http://piwik.org';
$externalReferrer = 'http://example.org';

$noReferrerKeyword = null;
$emptyReferrerKeyword = '';

return array(
// website referrer types usually do not have a keyword
array($noReferrerKeyword, $this->idSite1, $url, $externalReferrer),
// direct entries do usually not have a referrer keyowrd
array($noReferrerKeyword, $this->idSite1, $url, $directReferrer),

// it is a campaign but there is no referrer url and no keyword set specifically, we cannot detect a keyword
// it does not return null as it is converted to strlower(null)
array($emptyReferrerKeyword, $this->idSite1, $url . '?pk_campaign=test', $noReferrer),

// campaigns, coming from same domain should have a keyword
array('piwik.org', $this->idSite1, $url . '?pk_campaign=test', $directReferrer),
// campaigns, coming from different domain should have a keyword
array('example.org', $this->idSite2, $url . '?pk_campaign=test', $externalReferrer),
// campaign keyword is specifically set
array('campaignkey1', $this->idSite2, $url . '?pk_campaign=test&pk_keyword=campaignkey1', $externalReferrer),
array('campaignkey2', $this->idSite2, $url . '?pk_campaign=test&utm_term=campaignkey2', $externalReferrer),

// search engine should have keyword the search term
array('piwik', $this->idSite2, $url, 'http://google.com/search?q=piwik'),
);
}

private function getRequest($params)
{
return new Request($params);
}

private function getNewVisitor()
{
return new Visitor(new VisitProperties());
}

}
Expand Up @@ -74,7 +74,7 @@ public function getReferrerUrls()
$url = 'http://piwik.org/foo/bar';
$referrer = 'http://piwik.org';

$directEntryReferrerName = '';
$directEntryReferrerName = null;

return array(
// domain matches but path does not match for idsite1
Expand Down