1334 lines
51 KiB
PHP
Executable File
1334 lines
51 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Mautic\CampaignBundle\Controller;
|
|
|
|
use Doctrine\DBAL\Cache\CacheException;
|
|
use Doctrine\ORM\EntityManager;
|
|
use Doctrine\Persistence\ManagerRegistry;
|
|
use Mautic\AssetBundle\Event\AssetExportListEvent;
|
|
use Mautic\CampaignBundle\Entity\Campaign;
|
|
use Mautic\CampaignBundle\Entity\Event;
|
|
use Mautic\CampaignBundle\Entity\LeadEventLog;
|
|
use Mautic\CampaignBundle\Entity\LeadEventLogRepository;
|
|
use Mautic\CampaignBundle\Entity\Summary;
|
|
use Mautic\CampaignBundle\Entity\SummaryRepository;
|
|
use Mautic\CampaignBundle\EventCollector\EventCollector;
|
|
use Mautic\CampaignBundle\EventListener\CampaignActionJumpToEventSubscriber;
|
|
use Mautic\CampaignBundle\Model\CampaignModel;
|
|
use Mautic\CampaignBundle\Model\EventModel;
|
|
use Mautic\CoreBundle\Controller\AbstractStandardFormController;
|
|
use Mautic\CoreBundle\Event\EntityExportEvent;
|
|
use Mautic\CoreBundle\Factory\ModelFactory;
|
|
use Mautic\CoreBundle\Factory\PageHelperFactoryInterface;
|
|
use Mautic\CoreBundle\Form\Type\DateRangeType;
|
|
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
|
use Mautic\CoreBundle\Helper\DateTimeHelper;
|
|
use Mautic\CoreBundle\Helper\ExportHelper;
|
|
use Mautic\CoreBundle\Helper\UserHelper;
|
|
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
|
use Mautic\CoreBundle\Service\FlashBag;
|
|
use Mautic\CoreBundle\Translation\Translator;
|
|
use Mautic\CoreBundle\Twig\Helper\DateHelper;
|
|
use Mautic\FormBundle\Helper\FormFieldHelper;
|
|
use Mautic\LeadBundle\Controller\EntityContactsTrait;
|
|
use Psr\Log\LoggerInterface;
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
|
use Symfony\Component\Form\FormError;
|
|
use Symfony\Component\Form\FormFactoryInterface;
|
|
use Symfony\Component\Form\FormInterface;
|
|
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\RequestStack;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class CampaignController extends AbstractStandardFormController
|
|
{
|
|
use EntityContactsTrait;
|
|
|
|
/**
|
|
* @var array<string, mixed>
|
|
*/
|
|
protected array $campaignElements = [];
|
|
|
|
/**
|
|
* @var array<string, mixed>
|
|
*/
|
|
protected $addedSources = [];
|
|
|
|
/**
|
|
* @var array<string, mixed>
|
|
*/
|
|
protected $campaignEvents = [];
|
|
|
|
/**
|
|
* @var array<string, mixed>
|
|
*/
|
|
protected $campaignSources = [];
|
|
|
|
/**
|
|
* @var array<string, mixed>
|
|
*/
|
|
protected $connections = [];
|
|
|
|
/**
|
|
* @var array<string, mixed>
|
|
*/
|
|
protected $deletedEvents = [];
|
|
|
|
/**
|
|
* @var array<string, mixed>
|
|
*/
|
|
protected $deletedSources = [];
|
|
|
|
/**
|
|
* @var array<string, mixed>
|
|
*/
|
|
protected $listFilters = [];
|
|
|
|
/**
|
|
* @var array<string, mixed>
|
|
*/
|
|
protected $modifiedEvents = [];
|
|
|
|
protected $sessionId;
|
|
|
|
public function __construct(
|
|
FormFactoryInterface $formFactory,
|
|
FormFieldHelper $fieldHelper,
|
|
private EventCollector $eventCollector,
|
|
private DateHelper $dateHelper,
|
|
ManagerRegistry $managerRegistry,
|
|
ModelFactory $modelFactory,
|
|
UserHelper $userHelper,
|
|
CoreParametersHelper $coreParametersHelper,
|
|
EventDispatcherInterface $dispatcher,
|
|
Translator $translator,
|
|
FlashBag $flashBag,
|
|
private LoggerInterface $logger,
|
|
private RequestStack $requestStack,
|
|
CorePermissions $security,
|
|
private EntityManager $em,
|
|
) {
|
|
parent::__construct($formFactory, $fieldHelper, $managerRegistry, $modelFactory, $userHelper, $coreParametersHelper, $dispatcher, $translator, $flashBag, $requestStack, $security);
|
|
}
|
|
|
|
protected function getPermissions(): array
|
|
{
|
|
// set some permissions
|
|
return (array) $this->security->isGranted(
|
|
[
|
|
'campaign:campaigns:viewown',
|
|
'campaign:campaigns:viewother',
|
|
'campaign:campaigns:create',
|
|
'campaign:campaigns:editown',
|
|
'campaign:campaigns:editother',
|
|
'campaign:campaigns:cloneown',
|
|
'campaign:campaigns:cloneother',
|
|
'campaign:campaigns:deleteown',
|
|
'campaign:campaigns:deleteother',
|
|
'campaign:campaigns:publishown',
|
|
'campaign:campaigns:publishother',
|
|
],
|
|
'RETURN_ARRAY'
|
|
);
|
|
}
|
|
|
|
public function batchDeleteAction(Request $request): JsonResponse|RedirectResponse
|
|
{
|
|
return $this->batchDeleteStandard($request);
|
|
}
|
|
|
|
public function cloneAction(Request $request, $objectId): JsonResponse|RedirectResponse|Response
|
|
{
|
|
return $this->cloneStandard($request, $objectId);
|
|
}
|
|
|
|
/**
|
|
* @param array<int, mixed> $assetList
|
|
*/
|
|
private function handleExportDownload(
|
|
ExportHelper $exportHelper,
|
|
string $jsonOutput,
|
|
array $assetList,
|
|
string $exportFileName,
|
|
): JsonResponse|BinaryFileResponse {
|
|
$filePath = $exportHelper->writeToZipFile($jsonOutput, $assetList, '');
|
|
if (!file_exists($filePath)) {
|
|
$this->logger->error('Export file could not be created', ['filePath' => $filePath]);
|
|
$this->addFlashMessage('mautic.campaign.error.export.file_not_found', ['%path%' => $filePath], FlashBag::LEVEL_ERROR);
|
|
|
|
return new JsonResponse([
|
|
'error' => $this->translator->trans('mautic.campaign.error.export.file_not_found', ['%path%' => $filePath], 'flashes'),
|
|
'flashes' => $this->getFlashContent(),
|
|
], 400);
|
|
}
|
|
|
|
return $exportHelper->downloadAsZip($filePath, $exportFileName);
|
|
}
|
|
|
|
public function exportAction(ExportHelper $exportHelper, CampaignModel $campaignModel, int $objectId): JsonResponse|BinaryFileResponse|Response
|
|
{
|
|
if (!$this->security->isGranted('campaign:export:enable', 'MATCH_ONE')) {
|
|
$this->logger->error('Access denied for campaign export', ['user' => $this->user->getId()]);
|
|
|
|
return $this->accessDenied();
|
|
}
|
|
|
|
$campaign = $campaignModel->getEntity($objectId);
|
|
|
|
if (empty($campaign)) {
|
|
$this->logger->error('Campaign not found for export', ['objectId' => $objectId]);
|
|
|
|
return $this->notFound();
|
|
}
|
|
|
|
$date = (new \DateTimeImmutable())->format(DateTimeHelper::FORMAT_DB);
|
|
$exportFileName = $this->translator->trans('mautic.campaign.campaign_export_file.name', ['%date%' => $date]);
|
|
|
|
$event = new EntityExportEvent(Campaign::ENTITY_NAME, $objectId);
|
|
$event = $this->dispatcher->dispatch($event);
|
|
$data = $event->getEntities();
|
|
|
|
$jsonOutput = json_encode([$data], JSON_PRETTY_PRINT);
|
|
|
|
$assetListEvent = new AssetExportListEvent([$data]);
|
|
$assetListEvent = $this->dispatcher->dispatch($assetListEvent);
|
|
$assetList = $assetListEvent->getList();
|
|
|
|
return $this->handleExportDownload($exportHelper, $jsonOutput, $assetList, $exportFileName);
|
|
}
|
|
|
|
public function batchExportAction(Request $request, ExportHelper $exportHelper): JsonResponse|BinaryFileResponse|Response
|
|
{
|
|
// set some permissions
|
|
$permissions = $this->security->isGranted(
|
|
[
|
|
'campaign:campaigns:viewown',
|
|
'campaign:campaigns:viewother',
|
|
'campaign:campaigns:create',
|
|
'campaign:campaigns:editown',
|
|
'campaign:campaigns:editother',
|
|
'campaign:campaigns:deleteown',
|
|
'campaign:campaigns:deleteother',
|
|
],
|
|
'RETURN_ARRAY'
|
|
);
|
|
|
|
if (!$permissions['campaign:campaigns:viewown'] && !$permissions['campaign:campaigns:viewother']) {
|
|
return $this->accessDenied();
|
|
} elseif (!$this->security->isGranted('campaign:export:enable', 'MATCH_ONE')) {
|
|
return $this->accessDenied();
|
|
}
|
|
|
|
$session = $request->getSession();
|
|
$filter = $session->get('mautic.campaign.filter', '');
|
|
$orderByDir = $session->get('mautic.campaign.orderbydir', 'ASC');
|
|
|
|
$ids = $request->get('ids');
|
|
$date = (new \DateTimeImmutable())->format(DateTimeHelper::FORMAT_DB);
|
|
$exportFileName = $this->translator->trans('mautic.campaign.campaign_export_file.name', ['%date%' => $date]);
|
|
$objectIds = json_decode($ids, true);
|
|
|
|
if (empty($ids)) {
|
|
$repo = $this->em->getRepository(Campaign::class);
|
|
$repo->setTranslator($this->translator);
|
|
|
|
$args = [
|
|
'filter' => $filter,
|
|
'orderBy' => 'c.id',
|
|
'orderByDir' => $orderByDir,
|
|
'ignore_paginator' => true, // to get full result, not paginated
|
|
];
|
|
|
|
// Query campaigns
|
|
$campaigns = $repo->getEntities($args);
|
|
|
|
// Get campaign IDs
|
|
$objectIds = array_map(fn ($c) => $c->getId(), $campaigns);
|
|
}
|
|
$allData = [];
|
|
|
|
if (empty($objectIds)) {
|
|
$this->addFlashMessage('mautic.campaign.error.export.no_campaigns_selected', [], FlashBag::LEVEL_WARNING);
|
|
|
|
return new JsonResponse([
|
|
'error' => $this->translator->trans('mautic.campaign.error.export.no_campaigns_selected', [], 'flashes'),
|
|
'flashes' => $this->getFlashContent(),
|
|
], 400);
|
|
}
|
|
|
|
foreach ($objectIds as $objectId) {
|
|
$event = new EntityExportEvent(Campaign::ENTITY_NAME, (int) $objectId);
|
|
$event = $this->dispatcher->dispatch($event);
|
|
$data = $event->getEntities();
|
|
|
|
if (!empty($data)) {
|
|
$allData[] = $data;
|
|
}
|
|
}
|
|
|
|
$assetListEvent = new AssetExportListEvent($allData);
|
|
$assetListEvent = $this->dispatcher->dispatch($assetListEvent);
|
|
$assetList = $assetListEvent->getList();
|
|
|
|
$jsonOutput = json_encode($allData, JSON_PRETTY_PRINT);
|
|
|
|
return $this->handleExportDownload($exportHelper, $jsonOutput, $assetList, $exportFileName);
|
|
}
|
|
|
|
/**
|
|
* @param string|int $objectId
|
|
* @param int $page
|
|
* @param int|null $count
|
|
*
|
|
* @return JsonResponse|RedirectResponse|Response
|
|
*/
|
|
public function contactsAction(
|
|
Request $request,
|
|
PageHelperFactoryInterface $pageHelperFactory,
|
|
$objectId,
|
|
$page = 1,
|
|
$count = null,
|
|
?\DateTimeInterface $dateFrom = null,
|
|
?\DateTimeInterface $dateTo = null,
|
|
) {
|
|
$session = $request->getSession();
|
|
$session->set('mautic.campaign.contact.page', $page);
|
|
|
|
$permissions = [
|
|
'campaign:campaigns:view',
|
|
'lead:leads:viewown',
|
|
'lead:leads:viewother',
|
|
];
|
|
|
|
return $this->generateContactsGrid(
|
|
$request,
|
|
$pageHelperFactory,
|
|
$objectId,
|
|
$page,
|
|
$permissions,
|
|
'campaign',
|
|
'campaign_leads',
|
|
null,
|
|
'campaign_id',
|
|
['manually_removed' => 0],
|
|
null,
|
|
null,
|
|
[],
|
|
null,
|
|
'entity.lead_id',
|
|
'DESC',
|
|
$count,
|
|
$dateFrom,
|
|
$dateTo
|
|
);
|
|
}
|
|
|
|
public function EventStatsAction(int $objectId, string $dateFromValue, string $dateToValue): JsonResponse
|
|
{
|
|
$response = [];
|
|
$events = $this->getCampaignModel()->getEventRepository()->getCampaignEvents($objectId);
|
|
$dateFrom = null;
|
|
$dateTo = null;
|
|
$dateToPlusOne = null;
|
|
if ($this->coreParametersHelper->get('campaign_by_range')) {
|
|
$dateFrom = new \DateTimeImmutable($dateFromValue);
|
|
$dateTo = new \DateTimeImmutable($dateToValue);
|
|
$dateToPlusOne = $dateTo->modify('+1 day');
|
|
}
|
|
|
|
$leadCount = $this->getCampaignModel()->getRepository()->getCampaignLeadCount($objectId);
|
|
$logCounts = $this->processCampaignLogCounts($objectId, $dateFrom, $dateToPlusOne);
|
|
|
|
$campaignLogCounts = $logCounts['campaignLogCounts'] ?? [];
|
|
$campaignLogCountsProcessed = $logCounts['campaignLogCountsProcessed'] ?? [];
|
|
|
|
$this->processCampaignEvents($events, $leadCount, $campaignLogCounts, $campaignLogCountsProcessed);
|
|
$sortedEvents = $this->processCampaignEventsFromParentCondition($events);
|
|
|
|
$sourcesList = $this->getCampaignModel()->getSourceLists();
|
|
$campaign = $this->getCampaignModel()->getEntity($objectId);
|
|
$this->prepareCampaignSourcesForEdit($objectId, $sourcesList, true);
|
|
|
|
$response['preview'] = trim(
|
|
$this->renderView(
|
|
'@MauticCampaign/Campaign/_preview.html.twig',
|
|
[
|
|
'campaignId' => $objectId,
|
|
'campaign' => $campaign,
|
|
'campaignEvents' => $events,
|
|
'campaignSources' => $this->campaignSources,
|
|
'eventSettings' => $this->eventCollector->getEventsArray(),
|
|
'canvasSettings' => $campaign->getCanvasSettings(),
|
|
]
|
|
)
|
|
);
|
|
$response['decisions'] = trim($this->renderView('@MauticCampaign/Campaign/_events.html.twig', ['events' => $sortedEvents['decision']]));
|
|
$response['actions'] = trim($this->renderView('@MauticCampaign/Campaign/_events.html.twig', ['events' => $sortedEvents['action']]));
|
|
$response['conditions'] = trim($this->renderView('@MauticCampaign/Campaign/_events.html.twig', ['events' => $sortedEvents['condition']]));
|
|
|
|
return new JsonResponse(array_filter($response));
|
|
}
|
|
|
|
public function GraphAction(Request $request, int $objectId, string $dateFrom, string $dateTo): Response
|
|
{
|
|
$dateRangeValues = ['date_from' => $dateFrom, 'date_to' => $dateTo];
|
|
$action = $this->generateUrl('mautic_campaign_action', ['objectAction' => 'view', 'objectId' => $objectId]);
|
|
$dateRangeForm = $this->formFactory->create(DateRangeType::class, $dateRangeValues, ['action' => $action]);
|
|
$stats = $this->getCampaignModel()->getCampaignMetricsLineChartData(
|
|
null,
|
|
new \DateTime($dateRangeForm->get('date_from')->getData()),
|
|
new \DateTime($dateRangeForm->get('date_to')->getData()),
|
|
null,
|
|
['campaign_id' => $objectId]
|
|
);
|
|
|
|
return $this->ajaxAction(
|
|
$request,
|
|
[
|
|
'contentTemplate' => '@MauticCampaign/Campaign/graph.html.twig',
|
|
'viewParameters' => [
|
|
'campiagnId' => $objectId,
|
|
'stats' => $stats,
|
|
'dateRangeForm' => $dateRangeForm->createView(),
|
|
],
|
|
]
|
|
);
|
|
}
|
|
|
|
public function deleteAction(Request $request, $objectId): JsonResponse|RedirectResponse
|
|
{
|
|
return $this->deleteStandard($request, $objectId);
|
|
}
|
|
|
|
/**
|
|
* @param bool $ignorePost
|
|
*/
|
|
public function editAction(Request $request, $objectId, $ignorePost = false): JsonResponse|RedirectResponse|Response
|
|
{
|
|
return $this->editStandard($request, $objectId, $ignorePost);
|
|
}
|
|
|
|
/**
|
|
* @param int $page
|
|
*
|
|
* @return JsonResponse|Response
|
|
*/
|
|
public function indexAction(Request $request, $page = null)
|
|
{
|
|
// set some permissions
|
|
$permissions = $this->security->isGranted(
|
|
[
|
|
'campaign:campaigns:view',
|
|
'campaign:campaigns:viewown',
|
|
'campaign:campaigns:viewother',
|
|
'campaign:campaigns:create',
|
|
'campaign:campaigns:edit',
|
|
'campaign:campaigns:editown',
|
|
'campaign:campaigns:editother',
|
|
'campaign:campaigns:delete',
|
|
'campaign:campaigns:deleteown',
|
|
'campaign:campaigns:deleteother',
|
|
'campaign:campaigns:publish',
|
|
'campaign:campaigns:publishown',
|
|
'campaign:campaigns:publishother',
|
|
'campaign:imports:view',
|
|
'campaign:imports:create',
|
|
],
|
|
'RETURN_ARRAY',
|
|
null,
|
|
true
|
|
);
|
|
|
|
if (!$permissions['campaign:campaigns:view']) {
|
|
return $this->accessDenied();
|
|
}
|
|
|
|
$this->setListFilters();
|
|
|
|
$session = $request->getSession();
|
|
if (empty($page)) {
|
|
$page = $session->get('mautic.campaign.page', 1);
|
|
}
|
|
|
|
$limit = $session->get('mautic.campaign.limit', $this->coreParametersHelper->get('default_pagelimit'));
|
|
$start = (1 === $page) ? 0 : (($page - 1) * $limit);
|
|
if ($start < 0) {
|
|
$start = 0;
|
|
}
|
|
|
|
$search = $request->get('search', $session->get('mautic.campaign.filter', ''));
|
|
$session->set('mautic.campaign.filter', $search);
|
|
|
|
$filter = ['string' => $search, 'force' => []];
|
|
|
|
$model = $this->getModel('campaign');
|
|
|
|
if (!$permissions[$this->getPermissionBase().':viewother']) {
|
|
$filter['force'][] = ['column' => 'c.createdBy', 'expr' => 'eq', 'value' => $this->user->getId()];
|
|
}
|
|
|
|
$orderBy = $session->get('mautic.campaign.orderby', 'c.dateModified');
|
|
$orderByDir = $session->get('mautic.campaign.orderbydir', $this->getDefaultOrderDirection());
|
|
|
|
[$count, $items] = $this->getIndexItems($start, $limit, $filter, $orderBy, $orderByDir);
|
|
|
|
if ($count && $count < ($start + 1)) {
|
|
// the number of entities are now less then the current page so redirect to the last page
|
|
$lastPage = (1 === $count) ? 1 : (((ceil($count / $limit)) ?: 1) ?: 1);
|
|
|
|
$session->set('mautic.campaign.page', $lastPage);
|
|
$returnUrl = $this->generateUrl('mautic_campaign_index', ['page' => $lastPage]);
|
|
|
|
return $this->postActionRedirect(
|
|
$this->getPostActionRedirectArguments(
|
|
[
|
|
'returnUrl' => $returnUrl,
|
|
'viewParameters' => ['page' => $lastPage],
|
|
'contentTemplate' => 'Mautic\CampaignBundle\Controller\CampaignController::indexAction',
|
|
'passthroughVars' => [
|
|
'mauticContent' => 'campaign',
|
|
],
|
|
],
|
|
'index'
|
|
)
|
|
);
|
|
}
|
|
|
|
// set what page currently on so that we can return here after form submission/cancellation
|
|
$session->set('mautic.campaign.page', $page);
|
|
|
|
$viewParameters = [
|
|
'permissionBase' => $this->getPermissionBase(),
|
|
'mauticContent' => $this->getJsLoadMethodPrefix(),
|
|
'sessionVar' => $this->getSessionBase(),
|
|
'actionRoute' => $this->getActionRoute(),
|
|
'indexRoute' => $this->getIndexRoute(),
|
|
'tablePrefix' => $model->getRepository()->getTableAlias(),
|
|
'modelName' => $this->getModelName(),
|
|
'translationBase' => $this->getTranslationBase(),
|
|
'searchValue' => $search,
|
|
'items' => $items,
|
|
'totalItems' => $count,
|
|
'page' => $page,
|
|
'limit' => $limit,
|
|
'permissions' => $permissions,
|
|
'tmpl' => $request->get('tmpl', 'index'),
|
|
'enableExportPermission'=> $this->security->isAdmin() || $this->security->isGranted('campaign:export:enable', 'MATCH_ONE'),
|
|
];
|
|
|
|
return $this->delegateView(
|
|
$this->getViewArguments(
|
|
[
|
|
'viewParameters' => $viewParameters,
|
|
'contentTemplate' => '@MauticCampaign/Campaign/list.html.twig',
|
|
'passthroughVars' => [
|
|
'mauticContent' => $this->getJsLoadMethodPrefix(),
|
|
'route' => $this->generateUrl($this->getIndexRoute(), ['page' => $page]),
|
|
],
|
|
],
|
|
'index'
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Generates new form and processes post data.
|
|
*
|
|
* @return RedirectResponse|Response
|
|
*/
|
|
public function newAction(Request $request)
|
|
{
|
|
/** @var CampaignModel $model */
|
|
$model = $this->getModel('campaign');
|
|
$campaign = $model->getEntity();
|
|
|
|
if (!$this->security->isGranted('campaign:campaigns:create')) {
|
|
return $this->accessDenied();
|
|
}
|
|
|
|
// set the page we came from
|
|
$page = $request->getSession()->get('mautic.campaign.page', 1);
|
|
|
|
$options = $this->getEntityFormOptions();
|
|
$action = $this->generateUrl('mautic_campaign_action', ['objectAction' => 'new']);
|
|
$form = $model->createForm($campaign, $this->formFactory, $action, $options);
|
|
|
|
// /Check for a submitted form and process it
|
|
$isPost = 'POST' === $request->getMethod();
|
|
$this->beforeFormProcessed($campaign, $form, 'new', $isPost);
|
|
|
|
if ($isPost) {
|
|
$valid = false;
|
|
if (!$cancelled = $this->isFormCancelled($form)) {
|
|
if ($valid = $this->isFormValid($form)) {
|
|
if ($valid = $this->beforeEntitySave($campaign, $form, 'new')) {
|
|
$campaign->setDateModified(new \DateTime());
|
|
$model->saveEntity($campaign);
|
|
$this->afterEntitySave($campaign, $form, 'new', $valid);
|
|
|
|
if (method_exists($this, 'viewAction')) {
|
|
$viewParameters = ['objectId' => $campaign->getId(), 'objectAction' => 'view'];
|
|
$returnUrl = $this->generateUrl('mautic_campaign_action', $viewParameters);
|
|
$template = 'Mautic\CampaignBundle\Controller\CampaignController::viewAction';
|
|
} else {
|
|
$viewParameters = ['page' => $page];
|
|
$returnUrl = $this->generateUrl('mautic_campaign_index', $viewParameters);
|
|
$template = 'Mautic\CampaignBundle\Controller\CampaignController::indexAction';
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->afterFormProcessed($valid, $campaign, $form, 'new');
|
|
} else {
|
|
$viewParameters = ['page' => $page];
|
|
$returnUrl = $this->generateUrl($this->getIndexRoute(), $viewParameters);
|
|
$template = 'Mautic\CampaignBundle\Controller\CampaignController::indexAction';
|
|
}
|
|
|
|
$passthrough = [
|
|
'mauticContent' => 'cammpaign',
|
|
];
|
|
|
|
if ($isInPopup = isset($form['updateSelect'])) {
|
|
$template = false;
|
|
$passthrough = array_merge(
|
|
$passthrough,
|
|
$this->getUpdateSelectParams($form['updateSelect']->getData(), $campaign)
|
|
);
|
|
}
|
|
|
|
if ($cancelled || ($valid && !$this->isFormApplied($form))) {
|
|
if ($isInPopup) {
|
|
$passthrough['closeModal'] = true;
|
|
}
|
|
|
|
return $this->postActionRedirect(
|
|
$this->getPostActionRedirectArguments(
|
|
[
|
|
'returnUrl' => $returnUrl,
|
|
'viewParameters' => $viewParameters,
|
|
'contentTemplate' => $template,
|
|
'passthroughVars' => $passthrough,
|
|
'entity' => $campaign,
|
|
],
|
|
'new'
|
|
)
|
|
);
|
|
} elseif ($valid && $this->isFormApplied($form)) {
|
|
return $this->editAction($request, $campaign->getId(), true);
|
|
}
|
|
}
|
|
|
|
$delegateArgs = [
|
|
'viewParameters' => [
|
|
'permissionBase' => $model->getPermissionBase(),
|
|
'mauticContent' => 'campaign',
|
|
'actionRoute' => 'mautic_campaign_action',
|
|
'indexRoute' => 'mautic_campaign_index',
|
|
'tablePrefix' => 'c',
|
|
'modelName' => 'campaign',
|
|
'translationBase' => $this->getTranslationBase(),
|
|
'tmpl' => $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index',
|
|
'entity' => $campaign,
|
|
'form' => $this->getFormView($form, 'new'),
|
|
],
|
|
'contentTemplate' => '@MauticCampaign/Campaign/form.html.twig',
|
|
'passthroughVars' => [
|
|
'mauticContent' => 'campaign',
|
|
'route' => $this->generateUrl(
|
|
'mautic_campaign_action',
|
|
[
|
|
'objectAction' => (!empty($valid) ? 'edit' : 'new'), // valid means a new form was applied
|
|
'objectId' => ($campaign) ? $campaign->getId() : 0,
|
|
]
|
|
),
|
|
'validationError' => $this->getFormErrorForBuilder($form),
|
|
],
|
|
'entity' => $campaign,
|
|
'form' => $form,
|
|
];
|
|
|
|
return $this->delegateView(
|
|
$this->getViewArguments($delegateArgs, 'new')
|
|
);
|
|
}
|
|
|
|
public function viewAction(Request $request, $objectId): JsonResponse|Response
|
|
{
|
|
return $this->viewStandard($request, $objectId, $this->getModelName(), null, null, 'campaign');
|
|
}
|
|
|
|
/**
|
|
* @param Campaign $campaign
|
|
* @param Campaign $oldCampaign
|
|
*/
|
|
protected function afterEntityClone($campaign, $oldCampaign)
|
|
{
|
|
$tempId = 'mautic_'.sha1(uniqid(mt_rand(), true));
|
|
$objectId = $oldCampaign->getId();
|
|
|
|
// Get the events that need to be duplicated as well
|
|
$events = $oldCampaign->getEvents()->toArray();
|
|
|
|
$campaign->setIsPublished(false);
|
|
|
|
// Clone the campaign's events
|
|
/** @var Event $event */
|
|
foreach ($events as $event) {
|
|
$tempEventId = 'new'.$event->getId();
|
|
|
|
$clone = clone $event;
|
|
$clone->nullId();
|
|
$clone->setCampaign($campaign);
|
|
$clone->setTempId($tempEventId);
|
|
|
|
// Just wipe out the parent as it'll be generated when the cloned entity is saved
|
|
$clone->setParent(null);
|
|
|
|
if (CampaignActionJumpToEventSubscriber::EVENT_NAME === $clone->getType()) {
|
|
// Update properties to point to the new temp ID
|
|
$properties = $clone->getProperties();
|
|
$properties['jumpToEvent'] = 'new'.$properties['jumpToEvent'];
|
|
|
|
$clone->setProperties($properties);
|
|
}
|
|
|
|
$campaign->addEvent($tempEventId, $clone);
|
|
}
|
|
|
|
// Update canvas settings with new event ids
|
|
$canvasSettings = $campaign->getCanvasSettings();
|
|
if (isset($canvasSettings['nodes'])) {
|
|
foreach ($canvasSettings['nodes'] as &$node) {
|
|
// Only events and not lead sources
|
|
if (is_numeric($node['id'])) {
|
|
$node['id'] = 'new'.$node['id'];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isset($canvasSettings['connections'])) {
|
|
foreach ($canvasSettings['connections'] as &$c) {
|
|
// Only events and not lead sources
|
|
if (is_numeric($c['sourceId'])) {
|
|
$c['sourceId'] = 'new'.$c['sourceId'];
|
|
}
|
|
|
|
// Only events and not lead sources
|
|
if (is_numeric($c['targetId'])) {
|
|
$c['targetId'] = 'new'.$c['targetId'];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Simulate edit
|
|
$campaign->setCanvasSettings($canvasSettings);
|
|
$tempId = $this->getCampaignSessionId($campaign, 'clone', $tempId);
|
|
|
|
$campaignSources = $this->getCampaignModel()->getLeadSources($objectId);
|
|
$this->prepareCampaignSourcesForEdit($tempId, $campaignSources);
|
|
}
|
|
|
|
/**
|
|
* @param object $entity
|
|
* @param string $action
|
|
* @param bool|null $persistConnections
|
|
*/
|
|
protected function afterEntitySave($entity, FormInterface $form, $action, $persistConnections = null)
|
|
{
|
|
if ($persistConnections) {
|
|
// Update canvas settings with new event IDs then save
|
|
$this->connections = $this->getCampaignModel()->setCanvasSettings($entity, $this->connections);
|
|
} else {
|
|
// Just update and add to entity
|
|
$this->connections = $this->getCampaignModel()->setCanvasSettings($entity, $this->connections, false, $this->modifiedEvents);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param bool $isClone
|
|
*/
|
|
protected function afterFormProcessed($isValid, $entity, FormInterface $form, $action, $isClone = false)
|
|
{
|
|
if (!$isValid) {
|
|
// Add the canvas settings to the entity to be able to rebuild it
|
|
$this->afterEntitySave($entity, $form, $action, false);
|
|
} else {
|
|
$this->sessionId = $entity->getId();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is called before and after form is submitted.
|
|
*
|
|
* @param bool $isClone
|
|
*/
|
|
protected function beforeFormProcessed($entity, FormInterface $form, $action, $isPost, $objectId = null, $isClone = false)
|
|
{
|
|
$sessionId = $this->getCampaignSessionId($entity, $action, $objectId);
|
|
|
|
if ($isPost) {
|
|
// fetch data from form - use all() to get array data
|
|
$requestData = $this->requestStack->getCurrentRequest()->request->all();
|
|
$campaign = $requestData['campaign'] ?? [];
|
|
|
|
$campaignElements = $campaign['campaignElements'] ?? [];
|
|
|
|
// First load existing events to ensure we have complete data
|
|
if (!$isClone && $entity->getId()) {
|
|
$this->prepareCampaignEventsForEdit($entity, $sessionId, $isClone);
|
|
}
|
|
|
|
// set global elements (this may override some events with form data)
|
|
$this->setCampaignElements($campaignElements, $isClone);
|
|
|
|
$this->getCampaignModel()->setCanvasSettings($entity, $this->connections, false, $this->modifiedEvents);
|
|
$this->prepareCampaignSourcesForEdit($sessionId, $this->campaignSources, true);
|
|
} else {
|
|
if (!$isClone) {
|
|
// clear out existing fields in case the form was refreshed, browser closed, etc
|
|
$this->modifiedEvents = $this->campaignSources = [];
|
|
|
|
if ($entity->getId()) {
|
|
$campaignSources = $this->getCampaignModel()->getLeadSources($entity->getId());
|
|
$this->prepareCampaignSourcesForEdit($sessionId, $campaignSources);
|
|
} else {
|
|
$this->campaignElements['modifiedSources'] = [];
|
|
$this->campaignElements['campaignSources'] = [];
|
|
}
|
|
}
|
|
|
|
$this->deletedEvents = [];
|
|
$this->prepareCampaignEventsForEdit($entity, $sessionId, $isClone);
|
|
|
|
$form->get('sessionId')->setData($sessionId);
|
|
$this->campaignElements['canvasSettings'] = $entity->getCanvasSettings();
|
|
$form->get('campaignElements')->setData(json_encode($this->campaignElements));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Method to take JSON string or array of campaignElements and set all global variables.
|
|
*
|
|
* @param string|array<string, mixed> $campaignElements
|
|
*/
|
|
private function setCampaignElements(string|array $campaignElements, bool $isClone = false): void
|
|
{
|
|
// sets the global campaignElements
|
|
if (is_string($campaignElements)) {
|
|
$this->campaignElements = json_decode($campaignElements, true);
|
|
} else {
|
|
$this->campaignElements = $campaignElements;
|
|
}
|
|
|
|
// set added/updated events - comes from global campaignElements which was set above
|
|
$this->setCampaignEvents();
|
|
// set added/updated sources - comes from global campaignElements which was set above
|
|
$this->setCampaignSources($isClone);
|
|
|
|
$this->connections = $this->campaignElements['canvasSettings'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* @param Campaign $entity
|
|
* @param bool $isClone
|
|
*/
|
|
protected function beforeEntitySave($entity, FormInterface $form, $action, $objectId = null, $isClone = false): bool
|
|
{
|
|
if (empty($this->campaignEvents)) {
|
|
// set the error
|
|
$form->addError(
|
|
new FormError(
|
|
$this->translator->trans('mautic.campaign.form.events.notempty', [], 'validators')
|
|
)
|
|
);
|
|
|
|
return false;
|
|
}
|
|
|
|
if (empty($this->campaignSources['lists']) && empty($this->campaignSources['forms'])) {
|
|
// set the error
|
|
$form->addError(
|
|
new FormError(
|
|
$this->translator->trans('mautic.campaign.form.sources.notempty', [], 'validators')
|
|
)
|
|
);
|
|
|
|
return false;
|
|
}
|
|
|
|
if ($isClone) {
|
|
$this->setCampaignSources($isClone);
|
|
$this->getCampaignModel()->setLeadSources($entity, $this->campaignElements['campaignSources'], []);
|
|
// If this is a clone, we need to save the entity first to properly build the events, sources and canvas settings
|
|
$this->getCampaignModel()->getRepository()->saveEntity($entity);
|
|
// Set as new so that timestamps are still hydrated
|
|
$entity->setNew();
|
|
$this->sessionId = $entity->getId();
|
|
}
|
|
|
|
// Set lead sources
|
|
$this->getCampaignModel()->setLeadSources($entity, $this->addedSources, $this->deletedSources);
|
|
|
|
// Build and set Event entities
|
|
$this->getCampaignModel()->setEvents($entity, $this->campaignEvents, $this->connections, $this->deletedEvents);
|
|
|
|
if ('edit' === $action && null !== $this->connections) {
|
|
if (!empty($this->deletedEvents)) {
|
|
/** @var EventModel $eventModel */
|
|
$eventModel = $this->getModel('campaign.event');
|
|
$eventModel->deleteEvents($entity->getEvents()->toArray(), $this->deletedEvents);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return CampaignModel
|
|
*/
|
|
protected function getCampaignModel()
|
|
{
|
|
/** @var CampaignModel $model */
|
|
$model = $this->getModel($this->getModelName());
|
|
|
|
return $model;
|
|
}
|
|
|
|
/**
|
|
* @return int|string|null
|
|
*/
|
|
protected function getCampaignSessionId(Campaign $campaign, $action, $objectId = null)
|
|
{
|
|
if (isset($this->sessionId)) {
|
|
return $this->sessionId;
|
|
}
|
|
|
|
if ($objectId) {
|
|
$sessionId = $objectId;
|
|
} elseif ('new' === $action && empty($sessionId)) {
|
|
$sessionId = 'mautic_'.sha1(uniqid(mt_rand(), true));
|
|
if ($this->requestStack->getCurrentRequest()->request->has('campaign')) {
|
|
$campaign = $this->requestStack->getCurrentRequest()->request->all()['campaign'] ?? [];
|
|
$sessionId = $campaign['sessionId'] ?? $sessionId;
|
|
}
|
|
} elseif ('edit' === $action) {
|
|
$sessionId = $campaign->getId();
|
|
}
|
|
|
|
$this->sessionId = $sessionId;
|
|
|
|
return $sessionId;
|
|
}
|
|
|
|
protected function getTemplateBase(): string
|
|
{
|
|
return '@MauticCampaign/Campaign';
|
|
}
|
|
|
|
protected function getIndexItems($start, $limit, $filter, $orderBy, $orderByDir, array $args = [])
|
|
{
|
|
$session = $this->getCurrentRequest()->getSession();
|
|
$currentFilters = $session->get('mautic.campaign.list_filters', []);
|
|
$updatedFilters = $this->requestStack->getCurrentRequest()->get('filters', false);
|
|
|
|
$sourceLists = $this->getCampaignModel()->getSourceLists();
|
|
$listFilters = [
|
|
'filters' => [
|
|
'placeholder' => $this->translator->trans('mautic.campaign.filter.placeholder'),
|
|
'multiple' => true,
|
|
'groups' => [
|
|
'mautic.campaign.leadsource.form' => [
|
|
'options' => $sourceLists['forms'],
|
|
'prefix' => 'form',
|
|
],
|
|
'mautic.campaign.leadsource.list' => [
|
|
'options' => $sourceLists['lists'],
|
|
'prefix' => 'list',
|
|
],
|
|
],
|
|
],
|
|
];
|
|
|
|
if ($updatedFilters) {
|
|
// Filters have been updated
|
|
|
|
// Parse the selected values
|
|
$newFilters = [];
|
|
$updatedFilters = json_decode($updatedFilters, true);
|
|
|
|
if ($updatedFilters) {
|
|
foreach ($updatedFilters as $updatedFilter) {
|
|
[$clmn, $fltr] = explode(':', $updatedFilter);
|
|
|
|
$newFilters[$clmn][] = $fltr;
|
|
}
|
|
|
|
$currentFilters = $newFilters;
|
|
} else {
|
|
$currentFilters = [];
|
|
}
|
|
}
|
|
$session->set('mautic.campaign.list_filters', $currentFilters);
|
|
|
|
$joinLists = $joinForms = false;
|
|
if (!empty($currentFilters)) {
|
|
$listIds = $catIds = [];
|
|
foreach ($currentFilters as $type => $typeFilters) {
|
|
$listFilters['filters']['groups']['mautic.campaign.leadsource.'.$type]['values'] = $typeFilters;
|
|
|
|
foreach ($typeFilters as $fltr) {
|
|
if ('list' == $type) {
|
|
$listIds[] = (int) $fltr;
|
|
} else {
|
|
$formIds[] = (int) $fltr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($listIds)) {
|
|
$joinLists = true;
|
|
$filter['force'][] = ['column' => 'l.id', 'expr' => 'in', 'value' => $listIds];
|
|
}
|
|
|
|
if (!empty($formIds)) {
|
|
$joinForms = true;
|
|
$filter['force'][] = ['column' => 'f.id', 'expr' => 'in', 'value' => $formIds];
|
|
}
|
|
}
|
|
|
|
// Store for customizeViewArguments
|
|
$this->listFilters = $listFilters;
|
|
|
|
return parent::getIndexItems(
|
|
$start,
|
|
$limit,
|
|
$filter,
|
|
$orderBy,
|
|
$orderByDir,
|
|
[
|
|
'joinLists' => $joinLists,
|
|
'joinForms' => $joinForms,
|
|
]
|
|
);
|
|
}
|
|
|
|
protected function getModelName(): string
|
|
{
|
|
return 'campaign';
|
|
}
|
|
|
|
/**
|
|
* Set events from form data.
|
|
*/
|
|
private function setCampaignEvents(): void
|
|
{
|
|
$this->modifiedEvents = (array) ($this->campaignElements['modifiedEvents'] ?? []);
|
|
$this->deletedEvents = (array) ($this->campaignElements['deletedEvents'] ?? []);
|
|
$this->campaignEvents = array_diff_key($this->modifiedEvents, array_flip($this->deletedEvents));
|
|
}
|
|
|
|
/**
|
|
* Set sources from form data.
|
|
*/
|
|
private function setCampaignSources(bool $isClone = false): void
|
|
{
|
|
$campaignSources = (array) ($this->campaignElements['campaignSources'] ?? []);
|
|
$modifiedSources = (array) ($this->campaignElements['modifiedSources'] ?? []);
|
|
|
|
if ($campaignSources === $modifiedSources) {
|
|
if ($isClone) {
|
|
// Clone hasn't saved the sources yet so return the current list as added
|
|
$this->addedSources = $this->campaignSources = $campaignSources;
|
|
} else {
|
|
$this->campaignSources = $campaignSources;
|
|
}
|
|
} else {
|
|
// Deleted sources
|
|
foreach ($campaignSources as $type => $sources) {
|
|
if (isset($modifiedSources[$type])) {
|
|
$this->deletedSources[$type] = array_diff_key($sources, $modifiedSources[$type]);
|
|
} else {
|
|
$this->deletedSources[$type] = $sources;
|
|
}
|
|
}
|
|
|
|
// Added sources
|
|
foreach ($modifiedSources as $type => $sources) {
|
|
if (isset($campaignSources[$type])) {
|
|
$this->addedSources[$type] = array_diff_key($sources, $campaignSources[$type]);
|
|
} else {
|
|
$this->addedSources[$type] = $sources;
|
|
}
|
|
}
|
|
$this->campaignSources = $modifiedSources;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $action
|
|
*
|
|
* @throws CacheException
|
|
*/
|
|
protected function getViewArguments(array $args, $action): array
|
|
{
|
|
switch ($action) {
|
|
case 'index':
|
|
$args['viewParameters']['filters'] = $this->listFilters;
|
|
break;
|
|
case 'view':
|
|
/** @var Campaign $entity */
|
|
$entity = $args['entity'];
|
|
$objectId = $args['objectId'];
|
|
// Init the date range filter form
|
|
$dateRangeValues = $this->requestStack->getCurrentRequest()->get('daterange', []);
|
|
$action = $this->generateUrl('mautic_campaign_action', ['objectAction' => 'view', 'objectId' => $objectId]);
|
|
$dateRangeForm = $this->formFactory->create(DateRangeType::class, $dateRangeValues, ['action' => $action]);
|
|
$isEmailStatsEnabled = (bool) $this->coreParametersHelper->get('campaign_email_stats_enabled', true);
|
|
$showEmailStats = $isEmailStatsEnabled && $entity->isEmailCampaign();
|
|
|
|
$args['viewParameters'] = array_merge(
|
|
$args['viewParameters'],
|
|
[
|
|
'campaign' => $entity,
|
|
'sources' => $this->getCampaignModel()->getLeadSources($entity),
|
|
'showEmailStats' => $showEmailStats,
|
|
'dateRangeForm' => $dateRangeForm->createView(),
|
|
'campaignElements' => $this->campaignElements,
|
|
]
|
|
);
|
|
break;
|
|
case 'new':
|
|
case 'edit':
|
|
$session = $this->getCurrentRequest()->getSession();
|
|
$args['viewParameters'] = array_merge(
|
|
$args['viewParameters'],
|
|
[
|
|
'eventSettings' => $this->eventCollector->getEventsArray(),
|
|
'campaignEvents' => $this->campaignEvents,
|
|
'campaignSources' => $this->campaignSources,
|
|
'deletedEvents' => $this->deletedEvents,
|
|
'hasEventClone' => $session->has('mautic.campaign.events.clone.storage'),
|
|
'campaignElements' => $this->campaignElements,
|
|
]
|
|
);
|
|
break;
|
|
}
|
|
|
|
return $args;
|
|
}
|
|
|
|
/**
|
|
* @param bool $isClone
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function prepareCampaignEventsForEdit($entity, $objectId, $isClone = false)
|
|
{
|
|
// load existing events into session
|
|
$campaignEvents = [];
|
|
|
|
$existingEvents = $entity->getEvents()->toArray();
|
|
$translator = $this->translator;
|
|
foreach ($existingEvents as $e) {
|
|
// remove deleted events from existing events
|
|
if (!empty($e->getDeleted())) {
|
|
continue;
|
|
}
|
|
$event = $e->convertToArray();
|
|
if ($isClone) {
|
|
$id = $e->getTempId();
|
|
$event['id'] = $id;
|
|
} else {
|
|
$id = $e->getId();
|
|
}
|
|
|
|
unset($event['campaign']);
|
|
unset($event['children']);
|
|
unset($event['parent']);
|
|
unset($event['log']);
|
|
|
|
$label = false;
|
|
switch ($event['triggerMode']) {
|
|
case 'interval':
|
|
$label = $translator->trans(
|
|
'mautic.campaign.connection.trigger.interval.label'.('no' == $event['decisionPath'] ? '_inaction' : ''),
|
|
[
|
|
'%number%' => $event['triggerInterval'],
|
|
'%unit%' => $translator->trans(
|
|
'mautic.campaign.event.intervalunit.'.$event['triggerIntervalUnit'],
|
|
['%count%' => $event['triggerInterval']]
|
|
),
|
|
]
|
|
);
|
|
break;
|
|
case 'date':
|
|
$label = $translator->trans(
|
|
'mautic.campaign.connection.trigger.date.label'.('no' == $event['decisionPath'] ? '_inaction' : ''),
|
|
[
|
|
'%full%' => $this->dateHelper->toFull($event['triggerDate']),
|
|
'%time%' => $this->dateHelper->toTime($event['triggerDate']),
|
|
'%date%' => $this->dateHelper->toShort($event['triggerDate']),
|
|
]
|
|
);
|
|
break;
|
|
}
|
|
if ($label) {
|
|
$event['label'] = $label;
|
|
}
|
|
|
|
$campaignEvents[$id] = $event;
|
|
}
|
|
|
|
$this->modifiedEvents = $this->campaignEvents = $campaignEvents;
|
|
$this->campaignElements['modifiedEvents'] = $campaignEvents;
|
|
$this->campaignElements['campaignEvents'] = $campaignEvents;
|
|
}
|
|
|
|
protected function prepareCampaignSourcesForEdit($objectId, $campaignSources, $isPost = false)
|
|
{
|
|
$this->campaignSources = [];
|
|
if (is_array($campaignSources)) {
|
|
foreach ($campaignSources as $type => $sources) {
|
|
if (!empty($sources)) {
|
|
$campaignModel = $this->getModel('campaign');
|
|
\assert($campaignModel instanceof CampaignModel);
|
|
|
|
$sourceList = $campaignModel->getSourceLists($type);
|
|
$this->campaignSources[$type] = [
|
|
'sourceType' => $type,
|
|
'campaignId' => $objectId,
|
|
'names' => implode(', ', array_intersect_key($sourceList, $sources)),
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$isPost) {
|
|
$this->campaignElements['campaignSources'] = $campaignSources;
|
|
$this->campaignElements['modifiedSources'] = $campaignSources;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array<string, array<int|string, array<int|string, int|string>>>
|
|
*
|
|
* @throws CacheException
|
|
*/
|
|
private function processCampaignLogCounts(int $id, ?\DateTimeImmutable $dateFrom, ?\DateTimeImmutable $dateToPlusOne): array
|
|
{
|
|
if ($this->coreParametersHelper->get('campaign_use_summary')) {
|
|
/** @var SummaryRepository $summaryRepo */
|
|
$summaryRepo = $this->doctrine->getManager()->getRepository(Summary::class);
|
|
$campaignLogCounts = $summaryRepo->getCampaignLogCounts($id, $dateFrom, $dateToPlusOne);
|
|
$campaignLogCountsProcessed = $this->getCampaignLogCountsProcessed($campaignLogCounts);
|
|
} else {
|
|
/** @var LeadEventLogRepository $eventLogRepo */
|
|
$eventLogRepo = $this->doctrine->getManager()->getRepository(LeadEventLog::class);
|
|
$campaignLogCounts = $eventLogRepo->getCampaignLogCounts($id, false, false, false, $dateFrom, $dateToPlusOne);
|
|
$campaignLogCountsProcessed = $eventLogRepo->getCampaignLogCounts($id, true, false, false, $dateFrom, $dateToPlusOne);
|
|
}
|
|
|
|
return [
|
|
'campaignLogCounts' => $campaignLogCounts,
|
|
'campaignLogCountsProcessed' => $campaignLogCountsProcessed,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array<int|string, int|string>> $events
|
|
* @param array<int|string, array<int|string, int|string>> $campaignLogCounts
|
|
* @param array<int|string, array<int|string, int|string>> $campaignLogCountsProcessed
|
|
*/
|
|
private function processCampaignEvents(
|
|
array &$events,
|
|
int $leadCount,
|
|
array $campaignLogCounts,
|
|
array $campaignLogCountsProcessed,
|
|
): void {
|
|
foreach ($events as &$event) {
|
|
$event['logCountForPending'] =
|
|
$event['logCountProcessed'] =
|
|
$event['percent'] =
|
|
$event['yesPercent'] =
|
|
$event['noPercent'] = 0;
|
|
|
|
if (isset($campaignLogCounts[$event['id']])) {
|
|
$loggedCount = array_sum($campaignLogCounts[$event['id']]);
|
|
$logCountsProcessed = isset($campaignLogCountsProcessed[$event['id']]) ? array_sum($campaignLogCountsProcessed[$event['id']]) : 0;
|
|
$pending = $loggedCount - $logCountsProcessed;
|
|
$event['logCountForPending'] = $pending;
|
|
$event['logCountProcessed'] = $logCountsProcessed;
|
|
[$totalNo, $totalYes] = $campaignLogCounts[$event['id']];
|
|
$total = $totalYes + $totalNo;
|
|
|
|
if ($leadCount) {
|
|
$event['percent'] = min(100, max(0, round(($loggedCount / $total) * 100, 1)));
|
|
$event['yesPercent'] = min(100, max(0, round(($totalYes / $total) * 100, 1)));
|
|
$event['noPercent'] = min(100, max(0, round(($totalNo / $total) * 100, 1)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array<int|string, int|string>> $events
|
|
*
|
|
* @return array<string, array<int, array<int|string, int|string>>>
|
|
*/
|
|
private function processCampaignEventsFromParentCondition(array &$events): array
|
|
{
|
|
$sortedEvents = [
|
|
'decision' => [],
|
|
'action' => [],
|
|
'condition' => [],
|
|
];
|
|
|
|
// rewrite stats data from parent condition if exist
|
|
foreach ($events as &$event) {
|
|
if (!empty($event['decisionPath'])
|
|
&& !empty($event['parent_id'])
|
|
&& isset($events[$event['parent_id']])
|
|
&& 'condition' !== $event['eventType']) {
|
|
$parentEvent = $events[$event['parent_id']];
|
|
$event['percent'] = $parentEvent['percent'];
|
|
$event['yesPercent'] = $parentEvent['yesPercent'];
|
|
$event['noPercent'] = $parentEvent['noPercent'];
|
|
if ('yes' === $event['decisionPath']) {
|
|
$event['noPercent'] = 0;
|
|
} else {
|
|
$event['yesPercent'] = 0;
|
|
}
|
|
}
|
|
$sortedEvents[$event['eventType']][] = $event;
|
|
}
|
|
|
|
return $sortedEvents;
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array<int, string>> $campaignLogCounts
|
|
*
|
|
* @return array<int, array<int, string>>
|
|
*/
|
|
private function getCampaignLogCountsProcessed(array &$campaignLogCounts): array
|
|
{
|
|
$campaignLogCountsProcessed = [];
|
|
|
|
foreach ($campaignLogCounts as $eventId => $campaignLogCount) {
|
|
$campaignLogCountsProcessed[$eventId][] = $campaignLogCount[2];
|
|
unset($campaignLogCounts[$eventId][2]);
|
|
}
|
|
|
|
return $campaignLogCountsProcessed;
|
|
}
|
|
|
|
protected function getDefaultOrderDirection(): string
|
|
{
|
|
return 'DESC';
|
|
}
|
|
}
|