*/ class CampaignApiController extends CommonApiController { use LeadAccessTrait; /** * @var CampaignModel|null */ protected $model; public function __construct( CorePermissions $security, Translator $translator, EntityResultHelper $entityResultHelper, RouterInterface $router, FormFactoryInterface $formFactory, AppVersion $appVersion, private RequestStack $requestStack, private MembershipManager $membershipManager, ManagerRegistry $doctrine, ModelFactory $modelFactory, EventDispatcherInterface $dispatcher, CoreParametersHelper $coreParametersHelper, MauticFactory $factory ) { $campaignModel = $modelFactory->getModel('campaign'); \assert($campaignModel instanceof CampaignModel); $this->model = $campaignModel; $this->entityClass = Campaign::class; $this->entityNameOne = 'campaign'; $this->entityNameMulti = 'campaigns'; $this->permissionBase = 'campaign:campaigns'; $this->serializerGroups = ['campaignDetails', 'campaignEventDetails', 'categoryList', 'publishDetails', 'leadListList', 'formList']; parent::__construct($security, $translator, $entityResultHelper, $router, $formFactory, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper, $factory); } /** * Adds a lead to a campaign. * * @param int $id Campaign ID * @param int $leadId Lead ID * * @return Response * * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException */ public function addLeadAction($id, $leadId) { $entity = $this->model->getEntity($id); if (null !== $entity) { $leadModel = $this->getModel('lead'); $lead = $leadModel->getEntity($leadId); if (null == $lead) { return $this->notFound(); } elseif (!$this->security->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $lead->getOwner())) { return $this->accessDenied(); } $this->membershipManager->addContact($lead, $entity); $view = $this->view(['success' => 1], Response::HTTP_OK); return $this->handleView($view); } return $this->notFound(); } /** * Removes given lead from a campaign. * * @param int $id Campaign ID * @param int $leadId Lead ID * * @return Response * * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException */ public function removeLeadAction($id, $leadId) { $entity = $this->model->getEntity($id); if (null !== $entity) { $lead = $this->checkLeadAccess($leadId, 'edit'); if ($lead instanceof Response) { return $lead; } $this->membershipManager->removeContact($lead, $entity); $view = $this->view(['success' => 1], Response::HTTP_OK); return $this->handleView($view); } return $this->notFound(); } /** * @param Campaign &$entity * @param string $action */ protected function preSaveEntity(&$entity, $form, $parameters, $action = 'edit') { $method = $this->requestStack->getCurrentRequest()->getMethod(); if ('POST' === $method || 'PUT' === $method) { if (empty($parameters['events'])) { $msg = $this->translator->trans('mautic.campaign.form.events.notempty', [], 'validators'); return $this->returnError($msg, Response::HTTP_BAD_REQUEST); } elseif (empty($parameters['lists']) && empty($parameters['forms'])) { $msg = $this->translator->trans('mautic.campaign.form.sources.notempty', [], 'validators'); return $this->returnError($msg, Response::HTTP_BAD_REQUEST); } } $deletedSources = ['lists' => [], 'forms' => []]; $deletedEvents = []; $currentSources = [ 'lists' => isset($parameters['lists']) ? $this->modifyCampaignEventArray($parameters['lists']) : [], 'forms' => isset($parameters['forms']) ? $this->modifyCampaignEventArray($parameters['forms']) : [], ]; // delete events and sources which does not exist in the PUT request if ('PUT' === $method) { $requestEventIds = []; $requestSegmentIds = []; $requestFormIds = []; foreach ($parameters['events'] as $key => $requestEvent) { if (!isset($requestEvent['id'])) { return $this->returnError('$campaign[events]['.$key.']["id"] is missing', Response::HTTP_BAD_REQUEST); } $requestEventIds[] = $requestEvent['id']; } foreach ($entity->getEvents() as $currentEvent) { if (!in_array($currentEvent->getId(), $requestEventIds)) { $deletedEvents[] = $currentEvent->getId(); } } if (isset($parameters['lists'])) { foreach ($parameters['lists'] as $requestSegment) { if (!isset($requestSegment['id'])) { return $this->returnError('$campaign[lists]['.$key.']["id"] is missing', Response::HTTP_BAD_REQUEST); } $requestSegmentIds[] = $requestSegment['id']; } } foreach ($entity->getLists() as $currentSegment) { if (!in_array($currentSegment->getId(), $requestSegmentIds)) { $deletedSources['lists'][$currentSegment->getId()] = 'ignore'; } } if (isset($parameters['forms'])) { foreach ($parameters['forms'] as $requestForm) { if (!isset($requestForm['id'])) { return $this->returnError('$campaign[forms]['.$key.']["id"] is missing', Response::HTTP_BAD_REQUEST); } $requestFormIds[] = $requestForm['id']; } } foreach ($entity->getForms() as $currentForm) { if (!in_array($currentForm->getId(), $requestFormIds)) { $deletedSources['forms'][$currentForm->getId()] = 'ignore'; } } } // Set lead sources $this->model->setLeadSources($entity, $currentSources, $deletedSources); // Build and set Event entities if (isset($parameters['events']) && isset($parameters['canvasSettings'])) { $this->model->setEvents($entity, $parameters['events'], $parameters['canvasSettings'], $deletedEvents); } // Persist to the database before building connection so that IDs are available $this->model->saveEntity($entity); // Update canvas settings with new event IDs then save if (isset($parameters['canvasSettings'])) { $this->model->setCanvasSettings($entity, $parameters['canvasSettings']); } if (Request::METHOD_PUT === $method && !empty($deletedEvents)) { $campaignEventModel = $this->getModel('campaign.event'); \assert($campaignEventModel instanceof EventModel); $campaignEventModel->deleteEvents($entity->getEvents()->toArray(), $deletedEvents); } } /** * Change the array structure. * * @param array $events */ public function modifyCampaignEventArray($events): array { $updatedEvents = []; if ($events && is_array($events)) { foreach ($events as $event) { if (!empty($event['id'])) { $updatedEvents[$event['id']] = 'ignore'; } } } return $updatedEvents; } /** * Obtains a list of campaign contacts. * * @return Response */ public function getContactsAction(Request $request, $id) { $entity = $this->model->getEntity($id); if (null === $entity) { return $this->notFound(); } if (!$this->checkEntityAccess($entity)) { return $this->accessDenied(); } $where = InputHelper::clean($request->query->get('where') ?? []); $order = InputHelper::clean($request->query->get('order') ?? []); $start = (int) $request->query->get('start', 0); $limit = (int) $request->query->get('limit', 100); $where[] = [ 'col' => 'campaign_id', 'expr' => 'eq', 'val' => $id, ]; $where[] = [ 'col' => 'manually_removed', 'expr' => 'eq', 'val' => 0, ]; return $this->forward( 'Mautic\CoreBundle\Controller\Api\StatsApiController::listAction', [ 'table' => 'campaign_leads', 'itemsName' => 'contacts', 'order' => $order, 'where' => $where, 'start' => $start, 'limit' => $limit, ] ); } public function cloneCampaignAction($campaignId) { if (empty($campaignId) || false == intval($campaignId)) { return $this->notFound(); } $original = $this->model->getEntity($campaignId); if (empty($original)) { return $this->notFound(); } $entity = clone $original; if (!$this->checkEntityAccess($entity, 'create')) { return $this->accessDenied(); } $this->model->saveEntity($entity); $headers = []; // return the newly created entities location if applicable $route = 'mautic_api_campaigns_getone'; $headers['Location'] = $this->generateUrl( $route, array_merge(['id' => $entity->getId()], $this->routeParams), true ); $view = $this->view([$this->entityNameOne => $entity], Response::HTTP_OK, $headers); $this->setSerializationContext($view); return $this->handleView($view); } }