forked from vergnet/site-accueil-insa
295 lines
9.8 KiB
PHP
295 lines
9.8 KiB
PHP
<?php
|
|
/**
|
|
* Matomo - free/libre analytics platform
|
|
*
|
|
* @link https://matomo.org
|
|
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
|
*/
|
|
|
|
namespace Piwik\Plugins\CoreHome\Tracker;
|
|
|
|
use Piwik\Common;
|
|
use Piwik\Date;
|
|
use Piwik\EventDispatcher;
|
|
use Piwik\Exception\UnexpectedWebsiteFoundException;
|
|
use Piwik\Tracker\Cache;
|
|
use Piwik\Tracker\Request;
|
|
use Piwik\Tracker\RequestProcessor;
|
|
use Piwik\Tracker\Settings;
|
|
use Piwik\Tracker\TrackerConfig;
|
|
use Piwik\Tracker\Visit\VisitProperties;
|
|
use Piwik\Tracker\VisitExcluded;
|
|
use Piwik\Tracker\VisitorRecognizer;
|
|
use Piwik\Plugins\PrivacyManager\Config as PrivacyManagerConfig;
|
|
|
|
/**
|
|
* Encapsulates core tracking logic related to visits.
|
|
*
|
|
* ## Request Metadata
|
|
*
|
|
* This RequestProcessor exposes the following metadata for the **CoreHome** plugin:
|
|
*
|
|
* * **visitorId**: A hash that identifies the current visitor being tracked. This value is
|
|
* calculated using the Piwik\Tracker\Settings;:getConfigId() method.
|
|
*
|
|
* Set in `processRequestParams()`.
|
|
*
|
|
* * **isVisitorKnown**: True if the current visitor has visited the site before. False if
|
|
* otherwise.
|
|
*
|
|
* Set in `processRequestParams()`.
|
|
*
|
|
* * **isNewVisit**: True if the current action is the start of a new visit, false if it
|
|
* is part of an ongoing visit.
|
|
*
|
|
* Set in `processRequestParams()`. Other RequestProcessors can override
|
|
* this value to force a new visit or stop a new visit.
|
|
*
|
|
* * **visitorNotFoundInDb**: True if the current visit could not be updated.
|
|
*
|
|
* Set by the Visit object.
|
|
*/
|
|
class VisitRequestProcessor extends RequestProcessor
|
|
{
|
|
// TODO: much of the logic in this class should be moved to new service class
|
|
|
|
/**
|
|
* @var EventDispatcher
|
|
*/
|
|
private $eventDispatcher;
|
|
|
|
/**
|
|
* @var VisitorRecognizer
|
|
*/
|
|
private $visitorRecognizer;
|
|
|
|
/**
|
|
* @var Settings
|
|
*/
|
|
private $userSettings;
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
private $visitStandardLength;
|
|
|
|
/**
|
|
* Forces all requests to result in new visits. For debugging only.
|
|
*
|
|
* @var int
|
|
*/
|
|
private $trackerAlwaysNewVisitor;
|
|
|
|
public function __construct(EventDispatcher $eventDispatcher, VisitorRecognizer $visitorRecognizer, Settings $userSettings,
|
|
$visitStandardLength, $trackerAlwaysNewVisitor)
|
|
{
|
|
$this->eventDispatcher = $eventDispatcher;
|
|
$this->visitorRecognizer = $visitorRecognizer;
|
|
$this->userSettings = $userSettings;
|
|
$this->visitStandardLength = $visitStandardLength;
|
|
$this->trackerAlwaysNewVisitor = $trackerAlwaysNewVisitor;
|
|
}
|
|
|
|
public function processRequestParams(VisitProperties $visitProperties, Request $request)
|
|
{
|
|
// the IP is needed by isExcluded() and GoalManager->recordGoals()
|
|
$visitProperties->setProperty('location_ip', $request->getIp());
|
|
|
|
$excluded = new VisitExcluded($request);
|
|
$isExcluded = $excluded->isExcluded();
|
|
$request->setMetadata('CoreHome', 'isVisitExcluded', $isExcluded);
|
|
|
|
if ($isExcluded) {
|
|
return true;
|
|
}
|
|
|
|
$privacyConfig = new PrivacyManagerConfig();
|
|
|
|
$ip = $request->getIpString();
|
|
if ($privacyConfig->useAnonymizedIpForVisitEnrichment) {
|
|
$ip = $visitProperties->getProperty('location_ip');
|
|
}
|
|
|
|
// visitor recognition
|
|
$visitorId = $this->userSettings->getConfigId($request, $ip);
|
|
$request->setMetadata('CoreHome', 'visitorId', $visitorId);
|
|
|
|
$isKnown = $this->visitorRecognizer->findKnownVisitor($visitorId, $visitProperties, $request);
|
|
$request->setMetadata('CoreHome', 'isVisitorKnown', $isKnown);
|
|
|
|
$isNewVisit = $this->isVisitNew($visitProperties, $request, $this->visitorRecognizer->getLastKnownVisit());
|
|
$request->setMetadata('CoreHome', 'isNewVisit', $isNewVisit);
|
|
|
|
$request->setMetadata('CoreHome', 'lastKnownVisit', $this->visitorRecognizer->getLastKnownVisit());
|
|
|
|
if (!$isNewVisit) { // only copy over known visitor's information, if this is for an ongoing visit
|
|
$this->visitorRecognizer->updateVisitPropertiesFromLastVisitRow($visitProperties);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function afterRequestProcessed(VisitProperties $visitProperties, Request $request)
|
|
{
|
|
$ip = $visitProperties->getProperty('location_ip');
|
|
|
|
/**
|
|
* Triggered after visits are tested for exclusion so plugins can modify the IP address
|
|
* persisted with a visit.
|
|
*
|
|
* This event is primarily used by the **PrivacyManager** plugin to anonymize IP addresses.
|
|
*
|
|
* @param string &$ip The visitor's IP address.
|
|
*/
|
|
$this->eventDispatcher->postEvent('Tracker.setVisitorIp', array(&$ip));
|
|
|
|
$visitProperties->setProperty('location_ip', $ip);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* Note: public only for tests.
|
|
*
|
|
* @param VisitProperties $visitProperties The current visit/visitor information.
|
|
* @param Request $request
|
|
* @return bool
|
|
*/
|
|
public function isVisitNew(VisitProperties $visitProperties, Request $request, $lastKnownVisit)
|
|
{
|
|
$isKnown = $request->getMetadata('CoreHome', 'isVisitorKnown');
|
|
if (!$isKnown) {
|
|
return true;
|
|
}
|
|
|
|
$isNewVisitForced = $request->getParam('new_visit');
|
|
$isNewVisitForced = !empty($isNewVisitForced);
|
|
if($isNewVisitForced) {
|
|
Common::printDebug("-> New visit forced: &new_visit=1 in request");
|
|
return true;
|
|
}
|
|
|
|
if($this->trackerAlwaysNewVisitor) {
|
|
Common::printDebug("-> New visit forced: Debug.tracker_always_new_visitor = 1 in config.ini.php");
|
|
return true;
|
|
}
|
|
|
|
$isLastActionInTheSameVisit = $this->isLastActionInTheSameVisit($visitProperties, $request, $lastKnownVisit);
|
|
if (!$isLastActionInTheSameVisit) {
|
|
Common::printDebug("Visitor detected, but last action was more than 30 minutes ago...");
|
|
|
|
return true;
|
|
}
|
|
|
|
$wasLastActionYesterday = $this->wasLastActionNotToday($visitProperties, $request, $lastKnownVisit);
|
|
$forceNewVisitAtMidnight = (bool) TrackerConfig::getConfigValue('create_new_visit_after_midnight', $request->getIdSiteIfExists());
|
|
|
|
if ($wasLastActionYesterday && $forceNewVisitAtMidnight) {
|
|
Common::printDebug("Visitor detected, but last action was yesterday...");
|
|
|
|
return true;
|
|
}
|
|
|
|
if (!TrackerConfig::getConfigValue('enable_userid_overwrites_visitorid', $request->getIdSiteIfExists())
|
|
&& !$this->lastUserIdWasSetAndDoesMatch($visitProperties, $request)) {
|
|
Common::printDebug("Visitor detected, but last user_id does not match...");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the last action was done during the last 30 minutes
|
|
* @return bool
|
|
*/
|
|
protected function isLastActionInTheSameVisit(VisitProperties $visitProperties, Request $request, $lastKnownVisit)
|
|
{
|
|
$lastActionTime = $this->getLastKnownActionTime($visitProperties, $lastKnownVisit);
|
|
|
|
return isset($lastActionTime)
|
|
&& false !== $lastActionTime
|
|
&& ($lastActionTime > ($request->getCurrentTimestamp() - $this->visitStandardLength));
|
|
}
|
|
|
|
/**
|
|
* Returns true if the last action was not today.
|
|
* @param VisitProperties $visitor
|
|
* @return bool
|
|
*/
|
|
private function wasLastActionNotToday(VisitProperties $visitProperties, Request $request, $lastKnownVisit)
|
|
{
|
|
$lastActionTime = $this->getLastKnownActionTime($visitProperties, $lastKnownVisit);
|
|
|
|
if (empty($lastActionTime)) {
|
|
return false;
|
|
}
|
|
|
|
$idSite = $request->getIdSite();
|
|
$timezone = $this->getTimezoneForSite($idSite);
|
|
|
|
if (empty($timezone)) {
|
|
throw new UnexpectedWebsiteFoundException('An unexpected website was found, check idSite in the request');
|
|
}
|
|
|
|
$date = Date::factory((int)$lastActionTime, $timezone);
|
|
$now = $request->getCurrentTimestamp();
|
|
$now = Date::factory((int)$now, $timezone);
|
|
|
|
return $date->toString() !== $now->toString();
|
|
}
|
|
|
|
private function getTimezoneForSite($idSite) // TODO: duplicate function in Visit
|
|
{
|
|
try {
|
|
$site = Cache::getCacheWebsiteAttributes($idSite);
|
|
} catch (UnexpectedWebsiteFoundException $e) {
|
|
return null;
|
|
}
|
|
|
|
if (!empty($site['timezone'])) {
|
|
return $site['timezone'];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns the last action time for the last recorded visit of this visitor, or if the visitor is new,
|
|
* the current request's timestamp.
|
|
*
|
|
* @param VisitProperties $visitProperties
|
|
* @param $lastKnownVisit
|
|
* @return false|int|mixed
|
|
*/
|
|
private function getLastKnownActionTime(VisitProperties $visitProperties, $lastKnownVisit)
|
|
{
|
|
if (isset($lastKnownVisit['visit_last_action_time'])) {
|
|
return strtotime($lastKnownVisit['visit_last_action_time']);
|
|
}
|
|
|
|
return $visitProperties->getProperty('visit_last_action_time');
|
|
}
|
|
|
|
/**
|
|
* Returns true if the last user_id did not change.
|
|
* @return bool
|
|
*/
|
|
protected function lastUserIdWasSetAndDoesMatch(VisitProperties $visitProperties, Request $request)
|
|
{
|
|
$lastUserId = $visitProperties->getProperty('user_id');
|
|
|
|
if(empty($lastUserId)) {
|
|
return true;
|
|
}
|
|
|
|
$currentUserId = $request->getForcedUserId();
|
|
|
|
if(empty($currentUserId)) {
|
|
return true;
|
|
}
|
|
|
|
return $lastUserId === $currentUserId;
|
|
}
|
|
}
|