225 lines
7.5 KiB
PHP
Executable File
225 lines
7.5 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Mautic\LeadBundle\Helper;
|
|
|
|
use Mautic\CoreBundle\Helper\ClickthroughHelper;
|
|
use Mautic\CoreBundle\Helper\IpLookupHelper;
|
|
use Mautic\EmailBundle\Entity\Stat;
|
|
use Mautic\EmailBundle\Entity\StatRepository;
|
|
use Mautic\EmailBundle\Helper\BotRatioHelper;
|
|
use Mautic\LeadBundle\DataObject\LeadManipulator;
|
|
use Mautic\LeadBundle\Deduplicate\ContactMerger;
|
|
use Mautic\LeadBundle\Deduplicate\Exception\SameContactException;
|
|
use Mautic\LeadBundle\Entity\Lead;
|
|
use Mautic\LeadBundle\Event\ContactIdentificationEvent;
|
|
use Mautic\LeadBundle\Exception\ContactNotFoundException;
|
|
use Mautic\LeadBundle\LeadEvents;
|
|
use Mautic\LeadBundle\Model\LeadModel;
|
|
use Mautic\LeadBundle\Tracker\ContactTracker;
|
|
use Psr\Log\LoggerInterface;
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\RequestStack;
|
|
|
|
class ContactRequestHelper
|
|
{
|
|
/**
|
|
* @var Lead|null
|
|
*/
|
|
private $trackedContact;
|
|
|
|
private array $queryFields = [];
|
|
|
|
private array $publiclyUpdatableFieldValues = [];
|
|
|
|
public function __construct(
|
|
private LeadModel $leadModel,
|
|
private ContactTracker $contactTracker,
|
|
private IpLookupHelper $ipLookupHelper,
|
|
private RequestStack $requestStack,
|
|
private LoggerInterface $logger,
|
|
private EventDispatcherInterface $eventDispatcher,
|
|
private ContactMerger $contactMerger,
|
|
private StatRepository $statRepository,
|
|
private BotRatioHelper $botRatioHelper,
|
|
) {
|
|
}
|
|
|
|
public function getContactFromQuery(array $queryFields = []): ?Lead
|
|
{
|
|
$request = $this->getCurrentRequest();
|
|
if ($request && $request->cookies->get('Blocked-Tracking')) {
|
|
return null;
|
|
}
|
|
|
|
$ipAddress = $this->ipLookupHelper->getIpAddress();
|
|
if (!$ipAddress->isTrackable()) {
|
|
return null;
|
|
}
|
|
|
|
$dateTime = new \DateTime();
|
|
$userAgent = $request ? $request->server->get('HTTP_USER_AGENT') : '';
|
|
if (!empty($queryFields['ct'])) {
|
|
$queryFields['ct'] = (is_array($queryFields['ct'])) ? $queryFields['ct'] : ClickthroughHelper::decodeArrayFromUrl($queryFields['ct']);
|
|
}
|
|
|
|
if (isset($queryFields['ct']['stat'])) {
|
|
/** @var Stat $stat */
|
|
$stat = $this->statRepository->findOneBy(['trackingHash' => $queryFields['ct']['stat']]);
|
|
if (null !== $stat && $this->botRatioHelper->isHitByBot($stat, $dateTime, $ipAddress, (string) $userAgent)) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
unset($queryFields['page_url']); // This is set now automatically by PageModel
|
|
$this->queryFields = $queryFields;
|
|
|
|
try {
|
|
$foundContact = $this->getContactFromUrl();
|
|
$this->trackedContact = $foundContact;
|
|
$this->contactTracker->setTrackedContact($this->trackedContact);
|
|
} catch (ContactNotFoundException) {
|
|
}
|
|
|
|
if (!$this->trackedContact) {
|
|
$this->trackedContact = $this->contactTracker->getContact();
|
|
}
|
|
|
|
if (!$this->trackedContact) {
|
|
return null;
|
|
}
|
|
|
|
$this->prepareContactFromRequest();
|
|
|
|
return $this->trackedContact;
|
|
}
|
|
|
|
/**
|
|
* @throws ContactNotFoundException
|
|
*/
|
|
private function getContactFromUrl(): Lead
|
|
{
|
|
$request = $this->getCurrentRequest();
|
|
|
|
if ($request && $request->cookies->get('Blocked-Tracking')) {
|
|
throw new ContactNotFoundException();
|
|
}
|
|
|
|
// Check for a lead requested through clickthrough query parameter
|
|
if (isset($this->queryFields['ct'])) {
|
|
$clickthrough = (is_array($this->queryFields['ct'])) ? $this->queryFields['ct'] : ClickthroughHelper::decodeArrayFromUrl($this->queryFields['ct']);
|
|
} elseif ($request && $clickthrough = $request->get('ct', [])) {
|
|
$clickthrough = ClickthroughHelper::decodeArrayFromUrl($clickthrough);
|
|
}
|
|
|
|
if (!is_array($clickthrough)) {
|
|
throw new ContactNotFoundException();
|
|
}
|
|
|
|
try {
|
|
return $this->getContactFromClickthrough($clickthrough);
|
|
} catch (ContactNotFoundException) {
|
|
}
|
|
|
|
/* @var Lead $foundContact */
|
|
if (!empty($this->queryFields)) {
|
|
[$foundContact, $this->publiclyUpdatableFieldValues] = $this->leadModel->checkForDuplicateContact(
|
|
$this->queryFields,
|
|
true,
|
|
true
|
|
);
|
|
|
|
if ($this->trackedContact && $this->trackedContact->getId() && $foundContact->getId()) {
|
|
try {
|
|
$foundContact = $this->contactMerger->merge($this->trackedContact, $foundContact);
|
|
} catch (SameContactException) {
|
|
}
|
|
}
|
|
|
|
if (is_null($this->trackedContact) or $foundContact->getId() !== $this->trackedContact->getId()) {
|
|
// A contact was found by a publicly updatable field
|
|
if (!$foundContact->isNew()) {
|
|
return $foundContact;
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new ContactNotFoundException();
|
|
}
|
|
|
|
/**
|
|
* Identify a contact through a clickthrough URL.
|
|
*
|
|
* @return Lead
|
|
*
|
|
* @throws ContactNotFoundException
|
|
*/
|
|
private function getContactFromClickthrough(array $clickthrough)
|
|
{
|
|
$event = new ContactIdentificationEvent($clickthrough);
|
|
$this->eventDispatcher->dispatch($event, LeadEvents::ON_CLICKTHROUGH_IDENTIFICATION);
|
|
|
|
if ($contact = $event->getIdentifiedContact()) {
|
|
$this->logger->debug("LEAD: Contact ID# {$contact->getId()} tracked through clickthrough query by the ".$event->getIdentifier().' channel');
|
|
|
|
// Merge tracked visitor into the clickthrough contact
|
|
return $this->mergeWithTrackedContact($contact);
|
|
}
|
|
|
|
throw new ContactNotFoundException();
|
|
}
|
|
|
|
private function prepareContactFromRequest(): void
|
|
{
|
|
$ipAddress = $this->ipLookupHelper->getIpAddress();
|
|
$contactIpAddresses = $this->trackedContact->getIpAddresses();
|
|
if (!$contactIpAddresses->contains($ipAddress)) {
|
|
$this->trackedContact->addIpAddress($ipAddress);
|
|
}
|
|
|
|
if (!empty($this->publiclyUpdatableFieldValues)) {
|
|
$this->leadModel->setFieldValues(
|
|
$this->trackedContact,
|
|
$this->publiclyUpdatableFieldValues,
|
|
false,
|
|
true,
|
|
true
|
|
);
|
|
}
|
|
|
|
// Assume a web request as this is likely a tracking request from DWC or tracking code
|
|
$this->trackedContact->setManipulator(
|
|
new LeadManipulator(
|
|
'page',
|
|
'hit',
|
|
null,
|
|
$this->queryFields['page_url'] ?? ''
|
|
)
|
|
);
|
|
|
|
if (isset($this->queryFields['tags'])) {
|
|
$this->leadModel->modifyTags($this->trackedContact, $this->queryFields['tags']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return Lead
|
|
*/
|
|
private function mergeWithTrackedContact(Lead $foundContact)
|
|
{
|
|
if ($this->trackedContact && $this->trackedContact->getId() && $this->trackedContact->isAnonymous()) {
|
|
try {
|
|
return $this->contactMerger->merge($this->trackedContact, $foundContact);
|
|
} catch (SameContactException) {
|
|
}
|
|
}
|
|
|
|
return $foundContact;
|
|
}
|
|
|
|
private function getCurrentRequest(): ?Request
|
|
{
|
|
return $this->requestStack->getCurrentRequest();
|
|
}
|
|
}
|