Files
CloudOps/docker-compose/mautic-setup/mautic-backup-files/docroot/app/bundles/LeadBundle/Helper/ContactRequestHelper.php

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();
}
}