diff --git a/config/global.ini.php b/config/global.ini.php index 8fd053e3f7a44b31d38a046da64178431e585923..14675a3a9f1933f3cabf141290f333cd08bf9717 100644 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -220,6 +220,11 @@ latest_version_url = http://piwik.org/latest.zip api_service_url = http://api.piwik.org [Tracker] +; Piwik uses first party cookies by default. If set to 1, +; the visit ID cookie will be set on the Piwik server domain as well +; this is useful when you want to do cross websites analysis +use_third_party_cookies = 0 + ; set to 0 if you want to stop tracking the visitors. Useful if you need to stop all the connections on the DB. record_statistics = 1 @@ -229,10 +234,6 @@ visit_standard_length = 1800 ; visitors that stay on the website and view only one page will be considered as time on site of 0 second default_time_one_page_visit = 0 -; if set to 0, any goal conversion will be credited to the last more recent non empty referer. -; when set to 1, the first ever referer used to reach the website will be used -use_first_referer_to_determine_goal_referer = 0 - ; By default, Piwik does not trust the idcookie as accurate and will always check that if the visitor visited ; the website earlier by looking for a visitor with the same IP and user configuration (to avoid abuse or misbehaviour) ; This setting should only be set to 1 in an intranet setting, where most users have the same configuration (browsers, OS) diff --git a/core/Db/Schema/Myisam.php b/core/Db/Schema/Myisam.php index 287ee7aa592407406ca974ad03ffdff49e87e659..061a0f43b8a0781fa3c8cbb375f1752f9f5865d1 100644 --- a/core/Db/Schema/Myisam.php +++ b/core/Db/Schema/Myisam.php @@ -228,7 +228,6 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface server_time datetime NOT NULL, idaction_url int(11) default NULL, idlink_va int(11) default NULL, - referer_idvisit int(10) unsigned default NULL, referer_visit_server_date date default NULL, referer_type int(10) unsigned default NULL, referer_name varchar(70) default NULL, diff --git a/core/Tracker.php b/core/Tracker.php index d06529bf3db5e27d62ab6354f391d87ca7f5163e..9fec5f7d311d37159688e73781a0a6833606f9bb 100644 --- a/core/Tracker.php +++ b/core/Tracker.php @@ -33,14 +33,6 @@ class Piwik_Tracker const STATE_EMPTY_REQUEST = 11; const STATE_NOSCRIPT_REQUEST = 13; - const COOKIE_INDEX_IDVISITOR = 1; - const COOKIE_INDEX_REFERER_ID_VISIT = 6; - const COOKIE_INDEX_REFERER_TIMESTAMP = 7; - const COOKIE_INDEX_REFERER_TYPE = 8; - const COOKIE_INDEX_REFERER_NAME = 9; - const COOKIE_INDEX_REFERER_KEYWORD = 10; - const COOKIE_INDEX_VISITOR_RETURNING = 11; - // We use hex ID that are 16 chars in length, ie. 64 bits IDs const LENGTH_HEX_ID_STRING = 16; const LENGTH_BINARY_ID = 8; diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php index 0d6959af907dfa014f0eb5fe3b3877230f434e55..a391998eb02a74eba9ccea38ac199595ff0ce443 100644 --- a/core/Tracker/GoalManager.php +++ b/core/Tracker/GoalManager.php @@ -16,21 +16,12 @@ */ class Piwik_Tracker_GoalManager { - /** - * @var Piwik_Cookie - */ - protected $cookie = null; /** * @var Piwik_Tracker_Action */ protected $action = null; protected $convertedGoals = array(); - function setCookie($cookie) - { - $this->cookie = $cookie; - } - static public function getGoalDefinitions( $idSite ) { $websiteAttributes = Piwik_Common::getCacheWebsiteAttributes( $idSite ); @@ -181,29 +172,28 @@ class Piwik_Tracker_GoalManager 'server_time' => Piwik_Tracker::getDatetimeFromTimestamp($visitorInformation['visit_last_action_time']), 'location_country' => $location_country, 'location_continent'=> $location_continent, - 'visitor_returning' => $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_VISITOR_RETURNING ), + 'visitor_returning' => $visitorInformation['visitor_returning'], ); - $referer_idvisit = $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_REFERER_ID_VISIT ); - if($referer_idvisit !== false) + $refererTimestamp = Piwik_Common::getRequestVar('_refts', 0, 'int', $action->getRequest()); + $refererUrl = Piwik_Common::getRequestVar('_ref', '', 'string', $action->getRequest()); + $referrer = new Piwik_Tracker_Visit_Referer(); + $referrer = $referrer->getRefererInformation($refererUrl, $currentUrl = '', $idSite); + $goalData = array( + 'referer_visit_server_date' => date("Y-m-d", $refererTimestamp), + 'referer_type' => $referrer['referer_type'], + 'referer_name' => $referrer['referer_name'], + 'referer_keyword' => $referrer['referer_keyword'], + ); + + // Basic health check on the referer data + if($goalData['referer_type'] > 0 + && strlen($goalData['referer_name']) > 1 + // Cookie only lasts 6 months by default, so we shouldn't see anything older than 1 year + && $refererTimestamp > Piwik_Tracker::getCurrentTimestamp() - 365 * 86400 * 2 + && $refererTimestamp <= Piwik_Tracker::getCurrentTimestamp()) { - $refererTimestamp = (int)$this->cookie->get( Piwik_Tracker::COOKIE_INDEX_REFERER_TIMESTAMP ); - $goalData = array( - 'referer_idvisit' => (int)$referer_idvisit, - 'referer_visit_server_date' => date("Y-m-d", $refererTimestamp), - 'referer_type' => htmlspecialchars_decode($this->cookie->get( Piwik_Tracker::COOKIE_INDEX_REFERER_TYPE )), - 'referer_name' => htmlspecialchars_decode($this->cookie->get( Piwik_Tracker::COOKIE_INDEX_REFERER_NAME )), - 'referer_keyword' => htmlspecialchars_decode($this->cookie->get( Piwik_Tracker::COOKIE_INDEX_REFERER_KEYWORD )), - ); - - // Basic health check on the referer data - if($goalData['referer_idvisit'] > 0 - && $goalData['referer_type'] > 0 - && strlen($goalData['referer_name']) > 1 - && $refererTimestamp > Piwik_Tracker::getCurrentTimestamp() - 365.25 * 86400 * 2) - { - $goal += $goalData; - } + $goal += $goalData; } $goal += $visitCustomVariables; diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php index f5e886476c249a6af7026cfb4c2d046f1d4bbd58..5d9b3880fc956e6e0540dd2f8b4fbf06c0e38620 100644 --- a/core/Tracker/Visit.php +++ b/core/Tracker/Visit.php @@ -45,14 +45,6 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface protected $visitorKnown; protected $request; - // @see detect*() referer methods - protected $typeRefererAnalyzed; - protected $nameRefererAnalyzed; - protected $keywordRefererAnalyzed; - protected $refererHost; - protected $refererUrl; - protected $refererUrlParse; - protected $currentUrlParse; // can be overwritten in constructor protected $timestamp; @@ -84,8 +76,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface Piwik_PostEvent('Tracker.setRequest.idSite', $idsite); if($idsite <= 0) { - $idsite = Piwik_Common::getRequestVar('idsite', '', 'string', $this->request); - Piwik_Tracker_ExitWithException(new Exception('Invalid idsite: '.$idsite)); + Piwik_Tracker_ExitWithException(new Exception('Invalid idSite')); } $this->idsite = $idsite; @@ -224,12 +215,11 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface } // update the cookie with the new visit information - $this->updateCookie(); + $this->setThirdPartyCookie(); // record the goals if applicable if($someGoalsConverted) { - $goalManager->setCookie($this->cookie); $goalManager->recordGoals( $this->idsite, $this->visitorInfo, $this->visitorCustomVariables, $action); } unset($goalManager); @@ -394,7 +384,6 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface $serverTimestamp = $this->getCurrentTimestamp(); $idcookie = $this->getVisitorIdcookie(); - $returningVisitor = $this->isVisitorKnown() ? 1 : 0; $defaultTimeOnePageVisit = Piwik_Tracker_Config::getInstance()->Tracker['default_time_one_page_visit']; @@ -402,7 +391,11 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface $country = Piwik_Common::getCountry($userInfo['location_browser_lang'], $enableLanguageToCountryGuess = Piwik_Tracker_Config::getInstance()->Tracker['enable_language_to_country_guess'], $this->getVisitorIp()); - $refererInfo = $this->getRefererInformation(); + + $referrer = new Piwik_Tracker_Visit_Referer(); + $refererUrl = Piwik_Common::getRequestVar( 'urlref', '', 'string', $this->request); + $currentUrl = Piwik_Common::getRequestVar( 'url', '', 'string', $this->request); + $refererInfo = $referrer->getRefererInformation($refererUrl, $currentUrl, $this->idsite); /** * Save the visitor @@ -411,7 +404,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface 'idsite' => $this->idsite, 'visitor_localtime' => $localTime, 'idvisitor' => $idcookie, - 'visitor_returning' => $returningVisitor, + 'visitor_returning' => $this->isVisitorKnown() ? 1 : 0, 'visit_first_action_time' => Piwik_Tracker::getDatetimeFromTimestamp($serverTimestamp), 'visit_last_action_time' => Piwik_Tracker::getDatetimeFromTimestamp($serverTimestamp), 'visit_entry_idaction_url' => $idActionUrl, @@ -491,9 +484,9 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface } /** - * Returns vistor cookie + * Returns visitor cookie * - * @return string + * @return binary */ protected function getVisitorIdcookie() { @@ -691,6 +684,11 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface { return Piwik_Tracker_Config::getInstance()->Tracker['cookie_path']; } + + protected function shouldUseThirdPartyCookie() + { + return (bool)Piwik_Tracker_Config::getInstance()->Tracker['use_third_party_cookies']; + } /** * This methods tries to see if the visitor has visited the website before. @@ -727,17 +725,34 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface $this->setCookie( new Piwik_Cookie( $this->getCookieName(), $this->getCookieExpire(), - $this->getCookiePath(), - $key = $this->idsite ) ); + $this->getCookiePath()) ); $this->printCookie(); - // Does the idvisitor cookie contain a 16b string? - if( false !== ($idVisitor = $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_IDVISITOR )) - && strlen($idVisitor) == Piwik_Tracker::LENGTH_HEX_ID_STRING) + // Read ID Visitor + $found = false; + // - If set to use 3rd party cookies for Visit ID, read the cookies + // - By default, reads the first party cookie ID + $useThirdPartyCookie = $this->shouldUseThirdPartyCookie(); + if($useThirdPartyCookie) + { + $idVisitor = $this->cookie->get(0); + if($idVisitor !== false + && strlen($idVisitor) == Piwik_Tracker::LENGTH_HEX_ID_STRING) + { + $found = true; + } + } + else + { + $idVisitor = Piwik_Common::getRequestVar('_id', '', 'string', $this->request); + $found = strlen($idVisitor) >= Piwik_Tracker::LENGTH_HEX_ID_STRING; + } + + // Does the cookie contain a Visitor ID? + if( $found ) { $this->visitorInfo['idvisitor'] = Piwik_Common::hex2bin($idVisitor); - $this->visitorKnown = true; printDebug("The visitor has the piwik cookie (idvisitor = ".$idVisitor.") "); } else @@ -769,10 +784,10 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface visit_first_action_time, idvisit, visit_exit_idaction_url, - visit_exit_idaction_name + visit_exit_idaction_name, + visitor_returning FROM ".Piwik_Common::prefixTable('log_visit'). - " WHERE - ".$where." + " WHERE ".$where." ORDER BY visit_last_action_time DESC LIMIT 1"; $visitRow = Piwik_Tracker::getDatabase()->fetch($sql, $bindSql); @@ -781,12 +796,13 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface && $visitRow && count($visitRow) > 0) { - $this->visitorInfo['idvisitor'] = $visitRow['idvisitor']; $this->visitorInfo['visit_last_action_time'] = strtotime($visitRow['visit_last_action_time']); $this->visitorInfo['visit_first_action_time'] = strtotime($visitRow['visit_first_action_time']); + $this->visitorInfo['idvisitor'] = $visitRow['idvisitor']; $this->visitorInfo['idvisit'] = $visitRow['idvisit']; $this->visitorInfo['visit_exit_idaction_url'] = $visitRow['visit_exit_idaction_url']; $this->visitorInfo['visit_exit_idaction_name'] = $visitRow['visit_exit_idaction_name']; + $this->visitorInfo['visitor_returning'] = $visitRow['visitor_returning']; $this->visitorKnown = true; @@ -799,6 +815,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface else { printDebug("The visitor was not matched with an existing visitor..."); + $this->visitorKnown = false; } } @@ -948,38 +965,16 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface /** * Update the cookie information. */ - protected function updateCookie() + protected function setThirdPartyCookie() { - printDebug("We manage the cookie..."); - - // If new visit && not direct entry - if( isset($this->visitorInfo['referer_type']) - && $this->visitorInfo['referer_type'] != Piwik_Common::REFERER_TYPE_DIRECT_ENTRY) + if(!$this->shouldUseThirdPartyCookie()) { - // if the setting is set to use only the first referer, - // we don't update the cookie referer values if they are already set - if( !Piwik_Tracker_Config::getInstance()->Tracker['use_first_referer_to_determine_goal_referer'] - || $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_REFERER_TYPE ) == false) - { - $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_REFERER_TYPE, $this->visitorInfo['referer_type']); - $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_REFERER_NAME, $this->visitorInfo['referer_name']); - $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_REFERER_KEYWORD, $this->visitorInfo['referer_keyword']); - $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_REFERER_ID_VISIT, $this->visitorInfo['idvisit']); - $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_REFERER_TIMESTAMP, $this->getCurrentTimestamp()); - } + return; } - - // for a new visit, we flag the visit with visitor_returning - if(isset($this->visitorInfo['visitor_returning'])) - { - $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_VISITOR_RETURNING, - $this->visitorInfo['visitor_returning'] ); - } - - // idcookie has been generated in handleNewVisit or we simply propagate the old value - $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_IDVISITOR, - bin2hex($this->visitorInfo['idvisitor']) ); + printDebug("We manage the cookie..."); + // idcookie has been generated in handleNewVisit or we simply propagate the old value + $this->cookie->set(0, bin2hex($this->visitorInfo['idvisitor']) ); $this->cookie->save(); } @@ -1005,6 +1000,67 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface return $action; } + /** + * @param $action + * @return bool true if the outlink the visitor clicked on points to one of the known hosts for this website + */ + protected function detectActionIsOutlinkOnAliasHost(Piwik_Tracker_Action_Interface $action) + { + if($action->getActionType() != Piwik_Tracker_Action_Interface::TYPE_OUTLINK) + { + return false; + } + $actionUrl = $action->getActionUrl(); + $actionUrlParsed = @parse_url(Piwik_Common::unsanitizeInputValue($actionUrl)); + if(!isset($actionUrlParsed['host'])) + { + return false; + } + return $this->isHostKnownAliasHost($actionUrlParsed['host']); + } + + /** + * Returns a 64-bit hash of all the configuration settings + * @return string + */ + protected function getConfigHash( $os, $browserName, $browserVersion, $resolution, $plugin_Flash, $plugin_Java, $plugin_Director, $plugin_Quicktime, $plugin_RealPlayer, $plugin_PDF, $plugin_WindowsMedia, $plugin_Gears, $plugin_Silverlight, $plugin_Cookie, $ip, $browserLang) + { + $hash = md5( $os . $browserName . $browserVersion . $plugin_Flash . $plugin_Java . $plugin_Director . $plugin_Quicktime . $plugin_RealPlayer . $plugin_PDF . $plugin_WindowsMedia . $plugin_Gears . $plugin_Silverlight . $plugin_Cookie . $ip . $browserLang, $raw_output = true ); + return substr( $hash, 0, Piwik_Tracker::LENGTH_BINARY_ID); + } + + /** + * Returns either + * - "-1" for a known visitor + * - at least 16 char identifier in hex @see Piwik_Common::generateUniqId() + */ + protected function getVisitorUniqueId() + { + if($this->isVisitorKnown()) + { + return -1; + } + return Piwik_Common::generateUniqId(); + } + + protected function setCookie( $cookie ) + { + $this->cookie = $cookie; + } +} + +class Piwik_Tracker_Visit_Referer +{ + + // @see detect*() referer methods + protected $typeRefererAnalyzed; + protected $nameRefererAnalyzed; + protected $keywordRefererAnalyzed; + protected $refererHost; + protected $refererUrl; + protected $refererUrlParse; + protected $currentUrlParse; + /** * Returns an array containing the following information: * - referer_type @@ -1029,23 +1085,21 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface * * - referer_url : the same for all the referer types * + * @param URLs must be URL Encoded */ - public function getRefererInformation() + public function getRefererInformation($refererUrl, $currentUrl, $idSite) { + $this->idsite = $idSite; + // default values for the referer_* fields + $this->refererUrl = Piwik_Common::unsanitizeInputValue($refererUrl); + $this->refererUrlParse = @parse_url($this->refererUrl); + $this->currentUrlParse = @parse_url(Piwik_Common::unsanitizeInputValue($currentUrl)); $this->typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY; $this->nameRefererAnalyzed = ''; $this->keywordRefererAnalyzed = ''; $this->refererHost = ''; - // get the urls and parse them - $refererUrl = Piwik_Common::getRequestVar( 'urlref', '', 'string', $this->request); - $currentUrl = Piwik_Common::getRequestVar( 'url', '', 'string', $this->request); - - $this->refererUrl = $refererUrl; - $this->refererUrlParse = @parse_url(Piwik_Common::unsanitizeInputValue($refererUrl)); - $this->currentUrlParse = @parse_url(Piwik_Common::unsanitizeInputValue($currentUrl)); - if(isset($this->refererUrlParse['host'])) { $this->refererHost = $this->refererUrlParse['host']; @@ -1090,9 +1144,8 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface */ protected function detectRefererSearchEngine() { - $rawRefererUrl = Piwik_Common::unsanitizeInputValue($this->refererUrl); - $searchEngineInformation = Piwik_Common::extractSearchEngineInformationFromUrl($rawRefererUrl); - Piwik_PostEvent('Tracker.detectRefererSearchEngine', $searchEngineInformation, $rawRefererUrl); + $searchEngineInformation = Piwik_Common::extractSearchEngineInformationFromUrl($this->refererUrl); + Piwik_PostEvent('Tracker.detectRefererSearchEngine', $searchEngineInformation, $this->refererUrl); if($searchEngineInformation === false) { return false; @@ -1161,26 +1214,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface } return false; } - - /** - * @param $action - * @return bool true if the outlink the visitor clicked on points to one of the known hosts for this website - */ - protected function detectActionIsOutlinkOnAliasHost(Piwik_Tracker_Action_Interface $action) - { - if($action->getActionType() != Piwik_Tracker_Action_Interface::TYPE_OUTLINK) - { - return false; - } - $actionUrl = $action->getActionUrl(); - $actionUrlParsed = @parse_url(Piwik_Common::unsanitizeInputValue($actionUrl)); - if(!isset($actionUrlParsed['host'])) - { - return false; - } - return $this->isHostKnownAliasHost($actionUrlParsed['host']); - } - + // is the referer host any of the registered URLs for this website? protected function isHostKnownAliasHost($urlHost) { @@ -1199,38 +1233,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface } return false; } - - /** - * Returns a 64-bit hash of all the configuration settings - * @return string - */ - protected function getConfigHash( $os, $browserName, $browserVersion, $resolution, $plugin_Flash, $plugin_Java, $plugin_Director, $plugin_Quicktime, $plugin_RealPlayer, $plugin_PDF, $plugin_WindowsMedia, $plugin_Gears, $plugin_Silverlight, $plugin_Cookie, $ip, $browserLang) - { - $hash = md5( $os . $browserName . $browserVersion . $plugin_Flash . $plugin_Java . $plugin_Director . $plugin_Quicktime . $plugin_RealPlayer . $plugin_PDF . $plugin_WindowsMedia . $plugin_Gears . $plugin_Silverlight . $plugin_Cookie . $ip . $browserLang, $raw_output = true ); - return substr( $hash, 0, Piwik_Tracker::LENGTH_BINARY_ID); - } - - /** - * Returns either - * - "-1" for a known visitor - * - a unique 32 char identifier @see Piwik_Common::generateUniqId() - */ - protected function getVisitorUniqueId() - { - if($this->isVisitorKnown()) - { - return -1; - } - else - { - return Piwik_Common::generateUniqId(); - } - } - - protected function setCookie( $cookie ) - { - $this->cookie = $cookie; - } + } /** diff --git a/core/Updates/1.2.php b/core/Updates/1.2.php index 6711facecabfce59a6215de85a9c513f92ee4bfc..27dd657fbb8b257b31a3e96cbc882868d8ffbe2e 100644 --- a/core/Updates/1.2.php +++ b/core/Updates/1.2.php @@ -50,6 +50,7 @@ class Piwik_Updates_1_2 extends Piwik_Updates ' => false, 'ALTER TABLE `'. Piwik_Common::prefixTable('log_conversion') .'` + DROP `referer_idvisit`, ADD `idvisitor` BINARY(8) NOT NULL AFTER `idsite`, ADD custom_var_k1 VARCHAR(50) DEFAULT NULL, ADD custom_var_v1 VARCHAR(50) DEFAULT NULL, diff --git a/js/piwik.js b/js/piwik.js index 6555e15eb116c621524a6a05baeca0e6b82225e0..7fb3a7085e89a2c2b9b166e356f0a7e0a408fb21 100644 --- a/js/piwik.js +++ b/js/piwik.js @@ -1253,8 +1253,11 @@ var '&_ref=' + encodeWrapper(purify(referralUrl)) + '&_refts=' + referralTs + '&_viewts=' + lastVisitTs + - '&_cvar=' + customVariablesString + request; + if(customVariablesString.length > 5) { + request +='&_cvar=' + customVariablesString; + } + // Don't save in the cookie, the deleted custom variables for(cvarId in customVariables) {