395 lines
14 KiB
PHP
Executable File
395 lines
14 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Mautic\LeadBundle\Controller;
|
|
|
|
use Mautic\CoreBundle\Entity\AuditLogRepository;
|
|
use Mautic\CoreBundle\Helper\Chart\ChartQuery;
|
|
use Mautic\CoreBundle\Helper\Chart\LineChart;
|
|
use Mautic\CoreBundle\Model\AuditLogModel;
|
|
use Mautic\LeadBundle\Entity\Lead;
|
|
use Mautic\LeadBundle\Model\LeadModel;
|
|
use Symfony\Component\HttpFoundation\RequestStack;
|
|
|
|
trait LeadDetailsTrait
|
|
{
|
|
private ?RequestStack $requestStack = null;
|
|
|
|
/**
|
|
* @param int $page
|
|
*/
|
|
protected function getAllEngagements(array $leads, ?array $filters = null, ?array $orderBy = null, $page = 1, $limit = 25): array
|
|
{
|
|
$session = $this->requestStack->getCurrentRequest()->getSession();
|
|
|
|
if (null == $filters) {
|
|
$filters = $session->get(
|
|
'mautic.plugin.timeline.filters',
|
|
[
|
|
'search' => '',
|
|
'includeEvents' => [],
|
|
'excludeEvents' => [],
|
|
]
|
|
);
|
|
}
|
|
|
|
if (null == $orderBy) {
|
|
if (!$session->has('mautic.plugin.timeline.orderby')) {
|
|
$session->set('mautic.plugin.timeline.orderby', 'timestamp');
|
|
$session->set('mautic.plugin.timeline.orderbydir', 'DESC');
|
|
}
|
|
|
|
$orderBy = [
|
|
$session->get('mautic.plugin.timeline.orderby'),
|
|
$session->get('mautic.plugin.timeline.orderbydir'),
|
|
];
|
|
}
|
|
|
|
// prepare result object
|
|
$result = [
|
|
'events' => [],
|
|
'filters' => $filters,
|
|
'order' => $orderBy,
|
|
'types' => [],
|
|
'total' => 0,
|
|
'page' => $page,
|
|
'limit' => $limit,
|
|
'maxPages' => 0,
|
|
];
|
|
|
|
// get events for each contact
|
|
foreach ($leads as $lead) {
|
|
// if (!$lead->getEmail()) continue; // discard contacts without email
|
|
|
|
/** @var LeadModel $model */
|
|
$model = $this->getModel('lead');
|
|
$engagements = $model->getEngagements($lead, $filters, $orderBy, $page, $limit);
|
|
$events = $engagements['events'];
|
|
$types = $engagements['types'];
|
|
|
|
// inject lead into events
|
|
foreach ($events as &$event) {
|
|
$event['leadId'] = $lead->getId();
|
|
$event['leadEmail'] = $lead->getEmail();
|
|
$event['leadName'] = $lead->getName() ?: $lead->getEmail();
|
|
}
|
|
|
|
$result['events'] = array_merge($result['events'], $events);
|
|
$result['types'] = array_merge($result['types'], $types);
|
|
$result['total'] += $engagements['total'];
|
|
}
|
|
|
|
$result['maxPages'] = ($limit <= 0) ? 1 : round(ceil($result['total'] / $limit));
|
|
|
|
usort($result['events'], [$this, 'cmp']); // sort events by
|
|
|
|
// now all events are merged, let's limit to $limit
|
|
array_splice($result['events'], $limit);
|
|
|
|
$result['total'] = count($result['events']);
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Makes sure that the event filter array is in the right format.
|
|
*
|
|
* @param mixed $filters
|
|
*
|
|
* @return array
|
|
*
|
|
* @throws InvalidArgumentException if not an array
|
|
*/
|
|
public function sanitizeEventFilter($filters)
|
|
{
|
|
if (!is_array($filters)) {
|
|
throw new \InvalidArgumentException('filters parameter must be an array');
|
|
}
|
|
|
|
if (!isset($filters['search'])) {
|
|
$filters['search'] = '';
|
|
}
|
|
|
|
if (!isset($filters['includeEvents'])) {
|
|
$filters['includeEvents'] = [];
|
|
}
|
|
|
|
if (!isset($filters['excludeEvents'])) {
|
|
$filters['excludeEvents'] = [];
|
|
}
|
|
|
|
return $filters;
|
|
}
|
|
|
|
private function cmp($a, $b): int
|
|
{
|
|
return $b['timestamp'] <=> $a['timestamp'];
|
|
}
|
|
|
|
/**
|
|
* Get a list of places for the lead based on IP location.
|
|
*/
|
|
protected function getPlaces(Lead $lead): array
|
|
{
|
|
// Get Places from IP addresses
|
|
$places = [];
|
|
if ($lead->getIpAddresses()->count() > 0) {
|
|
foreach ($lead->getIpAddresses() as $ip) {
|
|
if ($details = $ip->getIpDetails()) {
|
|
if (!empty($details['latitude']) && !empty($details['longitude'])) {
|
|
$name = 'N/A';
|
|
if (!empty($details['city'])) {
|
|
$name = $details['city'];
|
|
} elseif (!empty($details['region'])) {
|
|
$name = $details['region'];
|
|
}
|
|
$place = [
|
|
'latLng' => [$details['latitude'], $details['longitude']],
|
|
'name' => $name,
|
|
];
|
|
$places[] = $place;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $places;
|
|
}
|
|
|
|
/**
|
|
* @return mixed[]
|
|
*/
|
|
protected function getEngagementData(Lead $lead, ?\DateTime $fromDate = null, ?\DateTime $toDate = null): array
|
|
{
|
|
$translator = $this->translator;
|
|
|
|
if (null == $fromDate) {
|
|
$fromDate = new \DateTime('first day of this month 00:00:00');
|
|
$fromDate->modify('-6 months');
|
|
}
|
|
if (null == $toDate) {
|
|
$toDate = new \DateTime();
|
|
}
|
|
|
|
$lineChart = new LineChart(null, $fromDate, $toDate);
|
|
$chartQuery = new ChartQuery($this->doctrine->getConnection(), $fromDate, $toDate);
|
|
|
|
/** @var LeadModel $model */
|
|
$model = $this->getModel('lead');
|
|
$engagements = $model->getEngagementCount($lead, $fromDate, $toDate, 'm', $chartQuery);
|
|
$lineChart->setDataset($translator->trans('mautic.lead.graph.line.all_engagements'), $engagements['byUnit']);
|
|
|
|
$pointStats = $chartQuery->fetchSumTimeData('lead_points_change_log', 'date_added', ['lead_id' => $lead->getId()], 'delta');
|
|
$lineChart->setDataset($translator->trans('mautic.lead.graph.line.points'), $pointStats);
|
|
|
|
return $lineChart->render();
|
|
}
|
|
|
|
/**
|
|
* @return mixed[]
|
|
*/
|
|
protected function getAuditlogs(Lead $lead, ?array $filters = null, ?array $orderBy = null, int $page = 1, int $limit = 25): array
|
|
{
|
|
$session = $this->requestStack->getCurrentRequest()->getSession();
|
|
|
|
if (null == $filters) {
|
|
$filters = $session->get(
|
|
'mautic.lead.'.$lead->getId().'.auditlog.filters',
|
|
[
|
|
'search' => '',
|
|
'includeEvents' => [],
|
|
'excludeEvents' => [],
|
|
]
|
|
);
|
|
}
|
|
|
|
if (null == $orderBy) {
|
|
if (!$session->has('mautic.lead.'.$lead->getId().'.auditlog.orderby')) {
|
|
$session->set('mautic.lead.'.$lead->getId().'.auditlog.orderby', 'al.dateAdded');
|
|
$session->set('mautic.lead.'.$lead->getId().'.auditlog.orderbydir', 'DESC');
|
|
}
|
|
|
|
$orderBy = [
|
|
$session->get('mautic.lead.'.$lead->getId().'.auditlog.orderby'),
|
|
$session->get('mautic.lead.'.$lead->getId().'.auditlog.orderbydir'),
|
|
];
|
|
}
|
|
|
|
// Audit Log
|
|
/** @var AuditLogModel $auditlogModel */
|
|
$auditlogModel = $this->getModel('core.auditlog');
|
|
/** @var AuditLogRepository $repo */
|
|
$repo = $auditlogModel->getRepository();
|
|
$logCount = $repo->getAuditLogsCount($lead, $filters);
|
|
$logs = $repo->getAuditLogs($lead, $filters, $orderBy, $page, $limit);
|
|
|
|
$logEvents = array_map(fn ($l): array => [
|
|
'eventType' => $l['action'],
|
|
'eventLabel' => $l['userName'],
|
|
'timestamp' => $l['dateAdded'],
|
|
'details' => $l['details'],
|
|
'contentTemplate' => '@MauticLead/Auditlog/details.html.twig',
|
|
], $logs);
|
|
|
|
$types = [
|
|
'delete' => $this->translator->trans('mautic.lead.event.delete'),
|
|
'create' => $this->translator->trans('mautic.lead.event.create'),
|
|
'identified' => $this->translator->trans('mautic.lead.event.identified'),
|
|
'ipadded' => $this->translator->trans('mautic.lead.event.ipadded'),
|
|
'merge' => $this->translator->trans('mautic.lead.event.merge'),
|
|
'update' => $this->translator->trans('mautic.lead.event.update'),
|
|
];
|
|
|
|
return [
|
|
'events' => $logEvents,
|
|
'filters' => $filters,
|
|
'order' => $orderBy,
|
|
'types' => $types,
|
|
'total' => $logCount,
|
|
'page' => $page,
|
|
'limit' => $limit,
|
|
'maxPages' => ceil($logCount / $limit),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param int $page
|
|
* @param int $limit
|
|
*/
|
|
protected function getEngagements(Lead $lead, ?array $filters = null, ?array $orderBy = null, $page = 1, $limit = 25): array
|
|
{
|
|
$session = $this->requestStack->getCurrentRequest()->getSession();
|
|
|
|
if (null == $filters) {
|
|
$filters = $session->get(
|
|
'mautic.lead.'.$lead->getId().'.timeline.filters',
|
|
[
|
|
'search' => '',
|
|
'includeEvents' => [],
|
|
'excludeEvents' => [],
|
|
]
|
|
);
|
|
}
|
|
|
|
if (null == $orderBy) {
|
|
if (!$session->has('mautic.lead.'.$lead->getId().'.timeline.orderby')) {
|
|
$session->set('mautic.lead.'.$lead->getId().'.timeline.orderby', 'timestamp');
|
|
$session->set('mautic.lead.'.$lead->getId().'.timeline.orderbydir', 'DESC');
|
|
}
|
|
|
|
$orderBy = [
|
|
$session->get('mautic.lead.'.$lead->getId().'.timeline.orderby'),
|
|
$session->get('mautic.lead.'.$lead->getId().'.timeline.orderbydir'),
|
|
];
|
|
}
|
|
/** @var LeadModel $model */
|
|
$model = $this->getModel('lead');
|
|
|
|
return $model->getEngagements($lead, $filters, $orderBy, $page, $limit);
|
|
}
|
|
|
|
/**
|
|
* Get an array with engagements and points of a contact.
|
|
*/
|
|
protected function getStatsCount(Lead $lead, ?\DateTime $fromDate = null, ?\DateTime $toDate = null): array
|
|
{
|
|
if (null == $fromDate) {
|
|
$fromDate = new \DateTime('first day of this month 00:00:00');
|
|
$fromDate->modify('-6 months');
|
|
}
|
|
if (null == $toDate) {
|
|
$toDate = new \DateTime();
|
|
}
|
|
|
|
/** @var LeadModel $model */
|
|
$model = $this->getModel('lead');
|
|
$chartQuery = new ChartQuery($this->doctrine->getConnection(), $fromDate, $toDate);
|
|
|
|
$engagements = $model->getEngagementCount($lead, $fromDate, $toDate, 'm', $chartQuery);
|
|
$pointStats = $chartQuery->fetchSumTimeData('lead_points_change_log', 'date_added', ['lead_id' => $lead->getId()], 'delta');
|
|
|
|
return [
|
|
'engagements' => $engagements,
|
|
'points' => $pointStats,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get an array to create company's engagements graph.
|
|
*
|
|
* @param array $contacts
|
|
*/
|
|
protected function getCompanyEngagementData($contacts): array
|
|
{
|
|
$engagements = [0, 0, 0, 0, 0, 0];
|
|
$points = [0, 0, 0, 0, 0, 0];
|
|
foreach ($contacts as $contact) {
|
|
/** @var LeadModel $model */
|
|
$model = $this->getModel('lead.lead');
|
|
|
|
if (!isset($contact['lead_id'])) {
|
|
continue;
|
|
}
|
|
|
|
$lead = $model->getEntity($contact['lead_id']);
|
|
|
|
if (!$lead instanceof Lead) {
|
|
continue;
|
|
}
|
|
$model->getRepository()->refetchEntity($lead);
|
|
$engagementsData = $this->getStatsCount($lead);
|
|
|
|
$engagements = array_map(fn ($a, $b) => $a + $b, $engagementsData['engagements']['byUnit'], $engagements);
|
|
$points = array_map(fn ($points_first_user, $points_second_user) => $points_first_user + $points_second_user, $engagementsData['points'], $points);
|
|
}
|
|
|
|
return [
|
|
'engagements' => $engagements,
|
|
'points' => $points,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get company graph for points and engagements.
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function getCompanyEngagementsForGraph($contacts): array
|
|
{
|
|
$graphData = $this->getCompanyEngagementData($contacts);
|
|
$translator = $this->translator;
|
|
|
|
$fromDate = new \DateTime('first day of this month 00:00:00');
|
|
$fromDate->modify('-6 months');
|
|
|
|
$toDate = new \DateTime();
|
|
|
|
$lineChart = new LineChart(null, $fromDate, $toDate);
|
|
|
|
$lineChart->setDataset($translator->trans('mautic.lead.graph.line.all_engagements'), $graphData['engagements']);
|
|
|
|
$lineChart->setDataset($translator->trans('mautic.lead.graph.line.points'), $graphData['points']);
|
|
|
|
return $lineChart->render();
|
|
}
|
|
|
|
protected function getScheduledCampaignEvents(Lead $lead): array
|
|
{
|
|
// Upcoming events from Campaign Bundle
|
|
/** @var \Mautic\CampaignBundle\Entity\LeadEventLogRepository $leadEventLogRepository */
|
|
$leadEventLogRepository = $this->doctrine->getManager()->getRepository(\Mautic\CampaignBundle\Entity\LeadEventLog::class);
|
|
|
|
return $leadEventLogRepository->getUpcomingEvents(
|
|
[
|
|
'lead' => $lead,
|
|
'eventType' => ['action', 'condition'],
|
|
]
|
|
);
|
|
}
|
|
|
|
#[\Symfony\Contracts\Service\Attribute\Required]
|
|
public function setRequestStackLeadDetailsTrait(?RequestStack $requestStack): void
|
|
{
|
|
$this->requestStack = $requestStack;
|
|
}
|
|
}
|