security->isGranted( [ 'lead:leads:viewown', 'lead:leads:viewother', 'lead:leads:create', 'lead:leads:editown', 'lead:leads:editother', 'lead:leads:deleteown', 'lead:leads:deleteother', 'lead:imports:view', 'lead:imports:create', ], 'RETURN_ARRAY' ); if (!$permissions['lead:leads:viewown'] && !$permissions['lead:leads:viewother']) { return $this->accessDenied(); } $this->setListFilters(); /** @var LeadModel $model */ $model = $this->getModel('lead'); $session = $request->getSession(); // set limits $limit = $session->get('mautic.lead.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.lead.filter', '')); $session->set('mautic.lead.filter', $search); // do some default filtering $orderBy = $session->get('mautic.lead.orderby', 'l.last_active'); // Add an id field to orderBy. Prevent Null-value ordering $orderById = 'l.id' !== $orderBy ? ', l.id' : ''; $orderBy = $orderBy.$orderById; $orderByDir = $session->get('mautic.lead.orderbydir', 'DESC'); $filter = ['string' => $search, 'force' => '']; $translator = $this->translator; $anonymous = $translator->trans('mautic.lead.lead.searchcommand.isanonymous'); $listCommand = $translator->trans('mautic.lead.lead.searchcommand.list'); $mine = $translator->trans('mautic.core.searchcommand.ismine'); $indexMode = $request->get('view', $session->get('mautic.lead.indexmode', 'list')); $session->set('mautic.lead.indexmode', $indexMode); $anonymousShowing = false; if ('list' != $indexMode || ('list' == $indexMode && !str_contains($search, $anonymous))) { // remove anonymous leads unless requested to prevent clutter $filter['force'] .= " !$anonymous"; } elseif (str_contains($search, $anonymous) && !str_contains($search, '!'.$anonymous)) { $anonymousShowing = true; } if (!$permissions['lead:leads:viewother']) { $filter['force'] .= " $mine"; } $results = $model->getEntities([ 'start' => $start, 'limit' => $limit, 'filter' => $filter, 'orderBy' => $orderBy, 'orderByDir' => $orderByDir, 'withTotalCount' => true, 'joinIpAddresses' => false, ]); $count = $results['count']; unset($results['count']); $leads = $results['results']; unset($results); if ($count && $count < ($start + 1)) { // the number of entities are now less then the current page so redirect to the last page if (1 === $count) { $lastPage = 1; } else { $lastPage = (ceil($count / $limit)) ?: 1; } $session->set('mautic.lead.page', $lastPage); $returnUrl = $this->generateUrl('mautic_contact_index', ['page' => $lastPage]); return $this->postActionRedirect( [ 'returnUrl' => $returnUrl, 'viewParameters' => ['page' => $lastPage], 'contentTemplate' => 'Mautic\LeadBundle\Controller\LeadController::indexAction', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'lead', ], ] ); } // set what page currently on so that we can return here after form submission/cancellation $session->set('mautic.lead.page', $page); $tmpl = $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index'; $listArgs = []; if (!$this->security->isGranted('lead:lists:viewother')) { $listArgs['filter']['force'] = " $mine"; } $leadListModel = $this->getModel('lead.list'); \assert($leadListModel instanceof ListModel); $lists = $leadListModel->getUserLists(); // check to see if in a single list $inSingleList = (1 === substr_count($search, "$listCommand:")) ? true : false; $list = []; if ($inSingleList) { preg_match("/$listCommand:(.*?)(?=\s|$)/", $search, $matches); if (!empty($matches[1])) { $alias = $matches[1]; foreach ($lists as $l) { if ($alias === $l['alias']) { $list = $l; break; } } } } // Get the max ID of the latest lead added $maxLeadId = $model->getRepository()->getMaxLeadId(); \assert($leadDNCModel instanceof \Mautic\LeadBundle\Model\DoNotContact); $dncRepository = $leadDNCModel->getDncRepo(); return $this->delegateView( [ 'viewParameters' => [ 'searchValue' => $search, 'columns' => $contactColumnsDictionary->getColumns(), 'items' => $leads, 'page' => $page, 'totalItems' => $count, 'limit' => $limit, 'permissions' => $permissions, 'tmpl' => $tmpl, 'indexMode' => $indexMode, 'lists' => $lists, 'currentList' => $list, 'security' => $this->security, 'inSingleList' => $inSingleList, 'noContactList' => $dncRepository->getChannelList(null, array_keys($leads)), 'maxLeadId' => $maxLeadId, 'anonymousShowing' => $anonymousShowing, ], 'contentTemplate' => "@MauticLead/Lead/{$indexMode}.html.twig", 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'lead', 'route' => $this->generateUrl('mautic_contact_index', ['page' => $page]), ], ] ); } public function quickAddAction(Request $request): Response { // set some permissions $permissions = $this->security->isGranted( [ 'lead:leads:viewown', 'lead:leads:viewother', 'lead:leads:create', 'lead:leads:editown', 'lead:leads:editother', ], 'RETURN_ARRAY' ); if ( !$permissions['lead:leads:viewown'] && !$permissions['lead:leads:viewother'] && !$permissions['lead:leads:create'] && !$permissions['lead:leads:editown'] && !$permissions['lead:leads:editother'] ) { return $this->accessDenied(); } /** @var LeadModel $model */ $model = $this->getModel('lead.lead'); // Get the quick add form $action = $this->generateUrl('mautic_contact_action', ['objectAction' => 'new', 'qf' => 1]); $fields = $this->getModel('lead.field')->getEntities( [ 'filter' => [ 'force' => [ [ 'column' => 'f.isPublished', 'expr' => 'eq', 'value' => true, ], [ 'column' => 'f.isShortVisible', 'expr' => 'eq', 'value' => true, ], [ 'column' => 'f.object', 'expr' => 'like', 'value' => 'lead', ], ], ], 'hydration_mode' => 'HYDRATE_ARRAY', 'result_cache' => new ResultCacheOptions(LeadField::CACHE_NAMESPACE), ] ); $quickForm = $model->createForm($model->getEntity(), $this->formFactory, $action, ['fields' => $fields, 'isShortForm' => true]); // set the default owner to the currently logged in user $currentUser = $this->get('security.token_storage')->getToken()->getUser(); $quickForm->get('owner')->setData($currentUser); if ($request->isMethod(Request::METHOD_POST)) { $quickForm->handleRequest($request); } return $this->delegateView( [ 'viewParameters' => [ 'quickForm' => $quickForm->createView(), ], 'contentTemplate' => '@MauticLead/Lead/quickadd.html.twig', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'lead', 'route' => false, ], ] ); } /** * Loads a specific lead into the detailed panel. * * @return JsonResponse|Response */ public function viewAction(Request $request, IntegrationHelper $integrationHelper, PointGroupModel $pointGroupModel, CoreParametersHelper $coreParametersHelper, $objectId) { /** @var LeadModel $model */ $model = $this->getModel('lead.lead'); $lead = $model->getEntity($objectId); if (null === $lead) { // get the page we came from $page = $request->getSession()->get('mautic.lead.page', 1); // set the return URL $returnUrl = $this->generateUrl('mautic_contact_index', ['page' => $page]); return $this->postActionRedirect( [ 'returnUrl' => $returnUrl, 'viewParameters' => ['page' => $page], 'contentTemplate' => 'Mautic\LeadBundle\Controller\LeadController::indexAction', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'contact', ], 'flashes' => [ [ 'type' => 'error', 'msg' => 'mautic.lead.lead.error.notfound', 'msgVars' => ['%id%' => $objectId], ], ], ] ); } /** @var Lead $lead */ $model->getRepository()->refetchEntity($lead); // set some permissions $permissions = $this->security->isGranted( [ 'lead:leads:viewown', 'lead:leads:viewother', 'lead:leads:create', 'lead:leads:editown', 'lead:leads:editother', 'lead:leads:deleteown', 'lead:leads:deleteother', ], 'RETURN_ARRAY' ); if (!$this->security->hasEntityAccess( 'lead:leads:viewown', 'lead:leads:viewother', $lead->getPermissionUser() ) ) { return $this->accessDenied(); } $fields = $lead->getFields(); $socialProfiles = (array) $integrationHelper->getUserProfiles($lead, $fields); $socialProfileUrls = $integrationHelper->getSocialProfileUrlRegex(false); $companyModel = $this->getModel('lead.company'); \assert($companyModel instanceof CompanyModel); $companiesRepo = $companyModel->getRepository(); $companies = $companiesRepo->getCompaniesByLeadId($objectId); // Set the social profile templates foreach ($socialProfiles as $integration => &$details) { if ($integrationObject = $integrationHelper->getIntegrationObject($integration)) { if ($template = $integrationObject->getSocialProfileTemplate()) { $details['social_profile_template'] = $template; } } if (!isset($details['social_profile_template'])) { // No profile template found unset($socialProfiles[$integration]); } } // We need the DoNotContact repository to check if a lead is flagged as do not contact $dnc = $this->doctrine->getManager()->getRepository(DoNotContact::class)->getEntriesByLeadAndChannel($lead, 'email'); $dncSms = $this->doctrine->getManager()->getRepository(DoNotContact::class)->getEntriesByLeadAndChannel($lead, 'sms'); $integrationRepo = $this->doctrine->getRepository(IntegrationEntity::class); $model = $this->getModel('lead.list'); \assert($model instanceof ListModel); $lists = $model->getRepository()->getLeadLists([$lead], true, true); $leadNoteModel = $this->getModel('lead.note'); \assert($leadNoteModel instanceof NoteModel); $leadDeviceRepository = $this->doctrine->getRepository(LeadDevice::class); return $this->delegateView( [ 'viewParameters' => [ 'lead' => $lead, 'avatarPanelState' => $request->cookies->get('mautic_lead_avatar_panel', 'expanded'), 'fields' => $fields, 'companies' => $companies, 'lists' => $lists, 'socialProfiles' => $socialProfiles, 'socialProfileUrls' => $socialProfileUrls, 'places' => $this->getPlaces($lead), 'permissions' => $permissions, 'events' => $this->getEngagements($lead), 'upcomingEvents' => $this->getScheduledCampaignEvents($lead), 'engagementData' => $this->getEngagementData($lead), 'noteCount' => $leadNoteModel->getNoteCount($lead, true), 'integrations' => $integrationRepo->getIntegrationEntityByLead($lead->getId()), 'devices' => $leadDeviceRepository->getLeadDevices($lead), 'auditlog' => $this->getAuditlogs($lead), 'doNotContact' => end($dnc), 'doNotContactSms' => end($dncSms), 'pointGroups' => $pointGroupModel->getEntities(), 'enableExportPermission' => $this->security->isAdmin() || $this->security->isGranted('lead:export:enable', 'MATCH_ONE'), // 'leadNotes' => $this->forward( // 'Mautic\LeadBundle\Controller\NoteController::indexAction', // [ // 'leadId' => $lead->getId(), // 'ignoreAjax' => 1, // ] // )->getContent(), ], 'allowMultipleCompanies' => $coreParametersHelper->get('contact_allow_multiple_companies'), 'contentTemplate' => '@MauticLead/Lead/lead.html.twig', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'lead', 'route' => $this->generateUrl( 'mautic_contact_action', [ 'objectAction' => 'view', 'objectId' => $lead->getId(), ] ), ], ] ); } /** * Generates new form and processes post data. * * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response */ public function newAction(Request $request, UserHelper $userHelper, AvatarHelper $avatarHelper) { /** @var LeadModel $model */ $model = $this->getModel('lead.lead'); $lead = $model->getEntity(); if (!$this->security->isGranted('lead:leads:create')) { return $this->accessDenied(); } // set the page we came from $page = $request->getSession()->get('mautic.lead.page', 1); $action = $this->generateUrl('mautic_contact_action', ['objectAction' => 'new']); $leadFieldModel = $this->getModel('lead.field'); \assert($leadFieldModel instanceof FieldModel); $fields = $leadFieldModel->getPublishedFieldArrays('lead'); $form = $model->createForm($lead, $this->formFactory, $action, ['fields' => $fields]); // /Check for a submitted form and process it if (Request::METHOD_POST === $request->getMethod()) { $valid = false; if (!$cancelled = $this->isFormCancelled($form)) { if ($valid = $this->isFormValid($form)) { // get custom field values $data = $request->request->get('lead'); // pull the data from the form in order to apply the form's formatting foreach ($form as $f) { if ('companies' !== $f->getName()) { $data[$f->getName()] = $f->getData(); } } $companies = []; if (isset($data['companies'])) { $companies = $data['companies']; unset($data['companies']); if (!is_array($companies)) { $companies = [$companies]; } } $model->setFieldValues($lead, $data, true); // form is valid so process the data $lead->setManipulator(new LeadManipulator( 'lead', 'lead', null, $userHelper->getUser()->getName() )); /** @var LeadRepository $contactRepository */ $contactRepository = $this->doctrine->getManager()->getRepository(Lead::class); // Save here as we need the entity with an ID for the company code bellow. $contactRepository->saveEntity($lead); if (!empty($companies)) { $model->modifyCompanies($lead, $companies); } // Save here through the model to trigger all subscribers. $model->saveEntity($lead); // Upload avatar if applicable $image = $form['preferred_profile_image']->getData(); if ('custom' === $image) { // Check for a file if ($form['custom_avatar']->getData()) { $this->uploadAvatar($request, $avatarHelper, $lead); } } $identifier = $this->translator->trans($lead->getPrimaryIdentifier()); $this->addFlashMessage( 'mautic.core.notice.created', [ '%name%' => $identifier, '%menu_link%' => 'mautic_contact_index', '%url%' => $this->generateUrl( 'mautic_contact_action', [ 'objectAction' => 'edit', 'objectId' => $lead->getId(), ] ), ] ); $inQuickForm = $request->get('qf', false); if ($inQuickForm) { $viewParameters = ['page' => $page]; $returnUrl = $this->generateUrl('mautic_contact_index', $viewParameters); $template = 'Mautic\LeadBundle\Controller\LeadController::indexAction'; } elseif ($this->getFormButton($form, ['buttons', 'save'])->isClicked()) { $viewParameters = [ 'objectAction' => 'view', 'objectId' => $lead->getId(), ]; $returnUrl = $this->generateUrl('mautic_contact_action', $viewParameters); $template = 'Mautic\LeadBundle\Controller\LeadController::viewAction'; } else { return $this->editAction($request, $userHelper, $avatarHelper, $lead->getId(), true); } } else { if ($request->get('qf', false)) { return $this->quickAddAction($request); } $formErrors = $this->getFormErrorMessages($form); $this->addFlashMessage( $this->getFormErrorMessage($formErrors), [], 'error' ); } } else { $viewParameters = ['page' => $page]; $returnUrl = $this->generateUrl('mautic_contact_index', $viewParameters); $template = 'Mautic\LeadBundle\Controller\LeadController::indexAction'; } if ($cancelled || $valid) { // cancelled or success return $this->postActionRedirect( [ 'returnUrl' => $returnUrl, 'viewParameters' => $viewParameters, 'contentTemplate' => $template, 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'lead', 'closeModal' => 1, // just in case in quick form ], ] ); } } else { // set the default owner to the currently logged in user $currentUser = $this->get('security.token_storage')->getToken()->getUser(); $form->get('owner')->setData($currentUser); } return $this->delegateView( [ 'viewParameters' => [ 'form' => $form->createView(), 'lead' => $lead, 'fields' => $model->organizeFieldsByGroup($fields), ], 'contentTemplate' => '@MauticLead/Lead/form.html.twig', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'lead', 'route' => $this->generateUrl( 'mautic_contact_action', [ 'objectAction' => 'new', ] ), ], ] ); } /** * Generates edit form. * * @param bool|false $ignorePost * * @return array|JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|Response */ public function editAction(Request $request, UserHelper $userHelper, AvatarHelper $avatarHelper, $objectId, $ignorePost = false) { /** @var LeadModel $model */ $model = $this->getModel('lead.lead'); $lead = $model->getEntity($objectId); // set the page we came from $page = $request->getSession()->get('mautic.lead.page', 1); // set the return URL $returnUrl = $this->generateUrl('mautic_contact_index', ['page' => $page]); $postActionVars = [ 'returnUrl' => $returnUrl, 'viewParameters' => ['page' => $page], 'contentTemplate' => 'Mautic\LeadBundle\Controller\LeadController::indexAction', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'lead', ], ]; // lead not found if (null === $lead) { return $this->postActionRedirect( array_merge( $postActionVars, [ 'flashes' => [ [ 'type' => 'error', 'msg' => 'mautic.lead.lead.error.notfound', 'msgVars' => ['%id%' => $objectId], ], ], ] ) ); } elseif (!$this->security->hasEntityAccess( 'lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser() ) ) { return $this->accessDenied(); } elseif ($model->isLocked($lead)) { // deny access if the entity is locked return $this->isLocked($postActionVars, $lead, 'lead.lead'); } $action = $this->generateUrl('mautic_contact_action', ['objectAction' => 'edit', 'objectId' => $objectId]); $leadFieldModel = $this->getModel('lead.field'); \assert($leadFieldModel instanceof FieldModel); $fields = $leadFieldModel->getPublishedFieldArrays('lead'); $form = $model->createForm($lead, $this->formFactory, $action, ['fields' => $fields]); // /Check for a submitted form and process it if (!$ignorePost && 'POST' === $request->getMethod()) { $valid = false; if (!$cancelled = $this->isFormCancelled($form)) { if ($valid = $this->isFormValid($form)) { $data = $request->request->get('lead'); // pull the data from the form in order to apply the form's formatting foreach ($form as $f) { if (('companies' !== $f->getName()) && ('company' !== $f->getName())) { $data[$f->getName()] = $f->getData(); } } $companies = []; if (isset($data['companies'])) { $companies = $data['companies']; unset($data['companies']); if (!is_array($companies)) { $companies = [$companies]; } } $model->setFieldValues($lead, $data, true); // form is valid so process the data $lead->setManipulator(new LeadManipulator( 'lead', 'lead', $objectId, $userHelper->getUser()->getName() )); $model->modifyCompanies($lead, $companies); $model->saveEntity($lead, $this->getFormButton($form, ['buttons', 'save'])->isClicked()); // Upload avatar if applicable $image = $form['preferred_profile_image']->getData(); if ('custom' == $image) { // Check for a file /** @var UploadedFile $file */ if ($file = $form['custom_avatar']->getData()) { $this->uploadAvatar($request, $avatarHelper, $lead); // Note the avatar update so that it can be forced to update $request->getSession()->set('mautic.lead.avatar.updated', true); } } $identifier = $this->translator->trans($lead->getPrimaryIdentifier()); $this->addFlashMessage( 'mautic.core.notice.updated', [ '%name%' => $identifier, '%menu_link%' => 'mautic_contact_index', '%url%' => $this->generateUrl( 'mautic_contact_action', [ 'objectAction' => 'edit', 'objectId' => $lead->getId(), ] ), ] ); } else { $formErrors = $this->getFormErrorMessages($form); $this->addFlashMessage( $this->getFormErrorMessage($formErrors), [], 'error' ); } } else { // unlock the entity $model->unlockEntity($lead); } if ($cancelled || ($valid && $this->getFormButton($form, ['buttons', 'save'])->isClicked())) { $viewParameters = [ 'objectAction' => 'view', 'objectId' => $lead->getId(), ]; return $this->postActionRedirect( array_merge( $postActionVars, [ 'returnUrl' => $this->generateUrl('mautic_contact_action', $viewParameters), 'viewParameters' => $viewParameters, 'contentTemplate' => 'Mautic\LeadBundle\Controller\LeadController::viewAction', ] ) ); } elseif ($valid) { // Refetch and recreate the form in order to populate data manipulated in the entity itself $lead = $model->getEntity($objectId); $form = $model->createForm($lead, $this->formFactory, $action, ['fields' => $fields]); } } else { // lock the entity $model->lockEntity($lead); } return $this->delegateView( [ 'viewParameters' => [ 'form' => $form->createView(), 'lead' => $lead, 'fields' => $lead->getFields(), // pass in the lead fields as they are already organized by ['group']['alias'] ], 'contentTemplate' => '@MauticLead/Lead/form.html.twig', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'lead', 'route' => $this->generateUrl( 'mautic_contact_action', [ 'objectAction' => 'edit', 'objectId' => $lead->getId(), ] ), ], ] ); } /** * Upload an asset. */ private function uploadAvatar(Request $request, AvatarHelper $avatarHelper, Lead $lead): void { $leadInformation = $request->files->get('lead', []); $file = $leadInformation['custom_avatar'] ?? null; $avatarDir = $avatarHelper->getAvatarPath(true); if (!file_exists($avatarDir)) { mkdir($avatarDir); } $file->move($avatarDir, 'avatar'.$lead->getId()); // remove the file from request $request->files->remove('lead'); } /** * Generates merge form and action. * * @return array|JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|Response */ public function mergeAction(Request $request, ContactMerger $contactMerger, $objectId) { /** @var LeadModel $model */ $model = $this->getModel('lead'); $mainLead = $model->getEntity($objectId); $page = $request->getSession()->get('mautic.lead.page', 1); // set the return URL $returnUrl = $this->generateUrl('mautic_contact_index', ['page' => $page]); $postActionVars = [ 'returnUrl' => $returnUrl, 'viewParameters' => ['page' => $page], 'contentTemplate' => 'Mautic\LeadBundle\Controller\LeadController::indexAction', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'lead', ], ]; if (null === $mainLead) { return $this->postActionRedirect( array_merge( $postActionVars, [ 'flashes' => [ [ 'type' => 'error', 'msg' => 'mautic.lead.lead.error.notfound', 'msgVars' => ['%id%' => $objectId], ], ], ] ) ); } // do some default filtering $session = $request->getSession(); $search = $request->get('search', $session->get('mautic.lead.merge.filter', '')); $session->set('mautic.lead.merge.filter', $search); $leads = []; if (!empty($search)) { $filter = [ 'string' => $search, 'force' => [ [ 'column' => 'l.date_identified', 'expr' => 'isNotNull', 'value' => $mainLead->getId(), ], [ 'column' => 'l.id', 'expr' => 'neq', 'value' => $mainLead->getId(), ], ], ]; $leads = $model->getEntities( [ 'limit' => 25, 'filter' => $filter, 'orderBy' => 'l.firstname,l.lastname,l.company,l.email', 'orderByDir' => 'ASC', 'withTotalCount' => false, ] ); } $leadChoices = []; foreach ($leads as $l) { $leadChoices[$l->getPrimaryIdentifier()] = $l->getId(); } $action = $this->generateUrl('mautic_contact_action', ['objectAction' => 'merge', 'objectId' => $mainLead->getId()]); $form = $this->formFactory->create( MergeType::class, [], [ 'action' => $action, 'leads' => $leadChoices, ] ); if ('POST' === $request->getMethod()) { $valid = true; if (!$this->isFormCancelled($form)) { if ($valid = $this->isFormValid($form)) { $data = $form->getData(); $secLeadId = $data['lead_to_merge']; $secLead = $model->getEntity($secLeadId); if (null === $secLead) { return $this->postActionRedirect( array_merge( $postActionVars, [ 'flashes' => [ [ 'type' => 'error', 'msg' => 'mautic.lead.lead.error.notfound', 'msgVars' => ['%id%' => $secLead->getId()], ], ], ] ) ); } elseif ( !$this->security->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $mainLead->getPermissionUser()) || !$this->security->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $secLead->getPermissionUser()) ) { return $this->accessDenied(); } elseif ($model->isLocked($mainLead)) { // deny access if the entity is locked return $this->isLocked($postActionVars, $secLead, 'lead'); } elseif ($model->isLocked($secLead)) { // deny access if the entity is locked return $this->isLocked($postActionVars, $secLead, 'lead'); } // Both leads are good so now we merge them try { $mainLead = $contactMerger->merge($mainLead, $secLead); } catch (SameContactException) { } } } if ($valid) { $viewParameters = [ 'objectId' => $mainLead->getId(), 'objectAction' => 'view', ]; return $this->postActionRedirect( [ 'returnUrl' => $this->generateUrl('mautic_contact_action', $viewParameters), 'viewParameters' => $viewParameters, 'contentTemplate' => 'Mautic\LeadBundle\Controller\LeadController::viewAction', 'passthroughVars' => [ 'closeModal' => 1, ], ] ); } } $tmpl = $request->get('tmpl', 'index'); return $this->delegateView( [ 'viewParameters' => [ 'tmpl' => $tmpl, 'leads' => $leads, 'searchValue' => $search, 'action' => $action, 'form' => $form->createView(), 'currentRoute' => $this->generateUrl( 'mautic_contact_action', [ 'objectAction' => 'merge', 'objectId' => $mainLead->getId(), ] ), ], 'contentTemplate' => '@MauticLead/Lead/merge.html.twig', 'passthroughVars' => [ 'route' => false, 'target' => ('update' == $tmpl) ? '.lead-merge-options' : null, ], ] ); } /** * Generates contact frequency rules form and action. * * @return array|JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|Response */ public function contactFrequencyAction(Request $request, $objectId) { /** @var LeadModel $model */ $model = $this->getModel('lead'); $lead = $model->getEntity($objectId); if (null === $lead || !$this->security->hasEntityAccess( 'lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser() ) ) { return $this->accessDenied(); } $viewParameters = [ 'objectId' => $lead->getId(), 'objectAction' => 'view', ]; $form = $this->getFrequencyRuleForm( $lead, $viewParameters, $data, false, $this->generateUrl('mautic_contact_action', ['objectAction' => 'contactFrequency', 'objectId' => $lead->getId()]) ); if (true === $form) { return $this->postActionRedirect( [ 'returnUrl' => $this->generateUrl('mautic_contact_action', [ 'objectId' => $lead->getId(), 'objectAction' => 'view', ]), 'viewParameters' => $viewParameters, 'contentTemplate' => 'Mautic\LeadBundle\Controller\LeadController::viewAction', 'passthroughVars' => [ 'closeModal' => 1, ], ] ); } $tmpl = $request->get('tmpl', 'index'); return $this->delegateView( [ 'viewParameters' => array_merge( [ 'tmpl' => $tmpl, 'form' => $form->createView(), 'currentRoute' => $this->generateUrl( 'mautic_contact_action', [ 'objectAction' => 'contactFrequency', 'objectId' => $lead->getId(), ] ), 'lead' => $lead, ], $viewParameters ), 'contentTemplate' => '@MauticLead/Lead/frequency.html.twig', 'passthroughVars' => [ 'route' => false, 'target' => ('update' == $tmpl) ? '.lead-frequency-options' : null, ], ] ); } /** * Deletes the entity. * * @return Response */ public function deleteAction(Request $request, $objectId) { $page = $request->getSession()->get('mautic.lead.page', 1); $returnUrl = $this->generateUrl('mautic_contact_index', ['page' => $page]); $flashes = []; $postActionVars = [ 'returnUrl' => $returnUrl, 'viewParameters' => ['page' => $page], 'contentTemplate' => 'Mautic\LeadBundle\Controller\LeadController::indexAction', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'lead', ], ]; if (Request::METHOD_POST === $request->getMethod()) { $model = $this->getModel('lead.lead'); \assert($model instanceof LeadModel); $entity = $model->getEntity($objectId); if (null === $entity) { $flashes[] = [ 'type' => 'error', 'msg' => 'mautic.lead.lead.error.notfound', 'msgVars' => ['%id%' => $objectId], ]; } elseif (!$this->security->hasEntityAccess( 'lead:leads:deleteown', 'lead:leads:deleteother', $entity->getPermissionUser() ) ) { return $this->accessDenied(); } elseif ($model->isLocked($entity)) { return $this->isLocked($postActionVars, $entity, 'lead.lead'); } else { $model->deleteEntity($entity); $identifier = $this->translator->trans($entity->getPrimaryIdentifier()); $flashes[] = [ 'type' => 'notice', 'msg' => 'mautic.core.notice.deleted', 'msgVars' => [ '%name%' => $identifier, '%id%' => $objectId, ], ]; } } // else don't do anything return $this->postActionRedirect( array_merge( $postActionVars, [ 'flashes' => $flashes, ] ) ); } /** * Deletes a group of entities. * * @return Response */ public function batchDeleteAction(Request $request) { $page = $request->getSession()->get('mautic.lead.page', 1); $returnUrl = $this->generateUrl('mautic_contact_index', ['page' => $page]); $flashes = []; $postActionVars = [ 'returnUrl' => $returnUrl, 'viewParameters' => ['page' => $page], 'contentTemplate' => 'Mautic\LeadBundle\Controller\LeadController::indexAction', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'lead', ], ]; if (Request::METHOD_POST === $request->getMethod()) { $model = $this->getModel('lead'); \assert($model instanceof LeadModel); $ids = json_decode($request->query->get('ids', '{}')); $deleteIds = []; // Loop over the IDs to perform access checks pre-delete foreach ($ids as $objectId) { $entity = $model->getEntity($objectId); if (null === $entity) { $flashes[] = [ 'type' => 'error', 'msg' => 'mautic.lead.lead.error.notfound', 'msgVars' => ['%id%' => $objectId], ]; } elseif (!$this->security->hasEntityAccess( 'lead:leads:deleteown', 'lead:leads:deleteother', $entity->getPermissionUser() ) ) { $flashes[] = $this->accessDenied(true); } elseif ($model->isLocked($entity)) { $flashes[] = $this->isLocked($postActionVars, $entity, 'lead', true); } else { $deleteIds[] = $objectId; } } // Delete everything we are able to if (!empty($deleteIds)) { $entities = $model->deleteEntities($deleteIds); $flashes[] = [ 'type' => 'notice', 'msg' => 'mautic.lead.lead.notice.batch_deleted', 'msgVars' => [ '%count%' => count($entities), ], ]; } } // else don't do anything return $this->postActionRedirect( array_merge( $postActionVars, [ 'flashes' => $flashes, ] ) ); } /** * Add/remove lead from a list. */ public function listAction($objectId): Response { /** @var LeadModel $model */ $model = $this->getModel('lead'); $lead = $model->getEntity($objectId); if (null != $lead && $this->security->hasEntityAccess( 'lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser() ) ) { /** @var ListModel $listModel */ $listModel = $this->getModel('lead.list'); $lists = $listModel->getUserLists(); // Get a list of lists for the lead $leadsLists = $model->getLists($lead, true, true); } else { $lists = $leadsLists = []; } return $this->delegateView( [ 'viewParameters' => [ 'lists' => $lists, 'leadsLists' => $leadsLists, 'lead' => $lead, ], 'contentTemplate' => '@MauticLead/LeadLists/index.html.twig', ] ); } /** * Add/remove lead from a company. */ public function companyAction($objectId): Response { /** @var LeadModel $model */ $model = $this->getModel('lead'); $lead = $model->getEntity($objectId); if (null != $lead && $this->security->hasEntityAccess( 'lead:leads:editown', 'lead:leads:editother', $lead->getOwner() ) ) { $companyModel = $this->getModel('lead.company'); \assert($companyModel instanceof CompanyModel); $companies = $companyModel->getUserCompanies(); // Get a list of lists for the lead $companyLeads = $lead->getCompanies(); foreach ($companyLeads as $cl) { $companyLead[$cl->getId()] = $cl->getId(); } } else { $companies = $companyLead = []; } return $this->delegateView( [ 'viewParameters' => [ 'companies' => $companies, 'companyLead' => $companyLead, 'lead' => $lead, ], 'contentTemplate' => '@MauticLead/Lead/company.html.twig', ] ); } /** * Add/remove lead from a campaign. */ public function campaignAction($objectId): Response { $model = $this->getModel('lead'); $lead = $model->getEntity($objectId); if (null != $lead && $this->security->hasEntityAccess( 'lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser() ) ) { /** @var \Mautic\CampaignBundle\Model\CampaignModel $campaignModel */ $campaignModel = $this->getModel('campaign'); $campaigns = $campaignModel->getPublishedCampaigns(true); $leadsCampaigns = $campaignModel->getLeadCampaigns($lead, true); foreach ($campaigns as $c) { $campaigns[$c['id']]['inCampaign'] = (isset($leadsCampaigns[$c['id']])) ? true : false; } } else { $campaigns = []; } return $this->delegateView( [ 'viewParameters' => [ 'campaigns' => $campaigns, 'lead' => $lead, ], 'contentTemplate' => '@MauticLead/LeadCampaigns/index.html.twig', ] ); } /** * @param int $objectId * * @return Response */ public function emailAction(Request $request, UserHelper $userHelper, MailHelper $mailHelper, $objectId = 0) { $valid = $cancelled = false; /** @var LeadModel $model */ $model = $this->getModel('lead'); /** @var Lead $lead */ $lead = $model->getEntity($objectId); if (null === $lead || !$this->security->hasEntityAccess( 'lead:leads:viewown', 'lead:leads:viewother', $lead->getPermissionUser() ) ) { return $this->modalAccessDenied(); } $leadFields = $lead->getProfileFields(); $leadFields['id'] = $lead->getId(); $leadEmail = $leadFields['email']; $leadName = $leadFields['firstname'].' '.$leadFields['lastname']; $mailerIsOwner = $this->coreParametersHelper->get('mailer_is_owner'); // Set onwer ID to be the current user ID so it will use his signature $leadFields['owner_id'] = $userHelper->getUser()->getId(); $inList = ('GET' === $request->getMethod()) ? $request->get('list', 0) : $request->request->get( 'lead_quickemail[list]', 0 ); $email = ['list' => $inList]; // Try set owner If should be mailer if ($lead->getOwner()) { $leadFields['owner_id'] = $lead->getOwner()->getId(); if ($mailerIsOwner) { $email['fromname'] = sprintf( '%s %s', $lead->getOwner()->getFirstName(), $lead->getOwner()->getLastName() ); $email['from'] = $lead->getOwner()->getEmail(); } } // Check if lead has a bounce status $dnc = $this->doctrine->getManager()->getRepository(DoNotContact::class)->getEntriesByLeadAndChannel($lead, 'email'); $action = $this->generateUrl('mautic_contact_action', ['objectAction' => 'email', 'objectId' => $objectId]); $form = $this->formFactory->create(EmailType::class, $email, ['action' => $action]); if ('POST' === $request->getMethod()) { $valid = false; if (!$cancelled = $this->isFormCancelled($form)) { if ($valid = $this->isFormValid($form)) { $email = $form->getData(); $bodyCheck = trim(strip_tags($email['body'])); if (!empty($bodyCheck)) { $mailer = $mailHelper->getMailer(); $mailer->addTo($leadEmail, $leadName); if (!empty($email[EmailType::REPLY_TO_ADDRESS])) { // The reply to address must be set into an email entity in order to take an effect. Otherwise it's overridden. $emailEntity = new Email(); $emailEntity->setSubject($email['subject']); $emailEntity->setReplyToAddress($email[EmailType::REPLY_TO_ADDRESS]); $mailer->setEmail($emailEntity); } $mailer->setFrom( $email['from'], empty($email['fromname']) ? null : $email['fromname'] ); // Set Content $mailer->setBody($email['body']); $mailer->parsePlainText($email['body']); $mailer->setLead($leadFields); $mailer->setIdHash(); $mailer->setSubject($email['subject']); // Ensure safe emoji for notification $subject = $email['subject']; if ($mailer->send(true, false)) { $mailer->createEmailStat(); $this->addFlashMessage( 'mautic.lead.email.notice.sent', [ '%subject%' => $subject, '%email%' => $leadEmail, ] ); } else { $errors = $mailer->getErrors(); // Unset the array of failed email addresses if (isset($errors['failures'])) { unset($errors['failures']); } $form->addError( new FormError( $this->translator->trans( 'mautic.lead.email.error.failed', [ '%subject%' => $subject, '%email%' => $leadEmail, '%error%' => implode('
', $errors), ], 'flashes' ) ) ); $valid = false; } } else { $form['body']->addError( new FormError( $this->translator->trans('mautic.lead.email.body.required', [], 'validators') ) ); $valid = false; } } } } if (empty($leadEmail) || $valid || $cancelled) { if ($inList) { $route = 'mautic_contact_index'; $viewParameters = [ 'page' => $request->getSession()->get('mautic.lead.page', 1), ]; $func = 'index'; } else { $route = 'mautic_contact_action'; $viewParameters = [ 'objectAction' => 'view', 'objectId' => $objectId, ]; $func = 'view'; } return $this->postActionRedirect( [ 'returnUrl' => $this->generateUrl($route, $viewParameters), 'viewParameters' => $viewParameters, 'contentTemplate' => 'Mautic\LeadBundle\Controller\LeadController::'.$func.'Action', 'passthroughVars' => [ 'mauticContent' => 'lead', 'closeModal' => 1, ], ] ); } return $this->ajaxAction( $request, [ 'contentTemplate' => '@MauticLead/Lead/email.html.twig', 'viewParameters' => [ 'form' => $form->createView(), 'dnc' => end($dnc), ], 'passthroughVars' => [ 'mauticContent' => 'leadEmail', 'route' => false, ], ] ); } /** * Bulk edit lead campaigns. * * @param int $objectId * * @return JsonResponse|Response */ public function batchCampaignsAction(Request $request, MembershipManager $membershipManager, $objectId = 0) { /** @var \Mautic\CampaignBundle\Model\CampaignModel $campaignModel */ $campaignModel = $this->getModel('campaign'); if ('POST' === $request->getMethod()) { /** @var LeadModel $model */ $model = $this->getModel('lead'); $data = $request->request->all()['lead_batch'] ?? []; $ids = json_decode($data['ids'], true); $entities = []; if (is_array($ids)) { $entities = $model->getEntities( [ 'filter' => [ 'force' => [ [ 'column' => 'l.id', 'expr' => 'in', 'value' => $ids, ], ], ], 'ignore_paginator' => true, ] ); } foreach ($entities as $key => $lead) { if (!$this->security->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser())) { unset($entities[$key]); } } $add = (!empty($data['add'])) ? $data['add'] : []; $remove = (!empty($data['remove'])) ? $data['remove'] : []; if ($count = count($entities)) { $campaigns = $campaignModel->getEntities( [ 'filter' => [ 'force' => [ [ 'column' => 'c.id', 'expr' => 'in', 'value' => array_merge($add, $remove), ], ], ], 'ignore_paginator' => true, ] ); if (!empty($add)) { foreach ($add as $cid) { $membershipManager->addContacts(new ArrayCollection($entities), $campaigns[$cid]); } } if (!empty($remove)) { foreach ($remove as $cid) { $membershipManager->removeContacts(new ArrayCollection($entities), $campaigns[$cid]); } } } $this->addFlashMessage( 'mautic.lead.batch_leads_affected', [ '%count%' => $count, ] ); return new JsonResponse( [ 'closeModal' => true, 'flashes' => $this->getFlashContent(), ] ); } else { // Get a list of campaigns $campaigns = $campaignModel->getPublishedCampaigns(true); $items = []; foreach ($campaigns as $campaign) { $items[$campaign['name']] = $campaign['id']; } $route = $this->generateUrl( 'mautic_contact_action', [ 'objectAction' => 'batchCampaigns', ] ); return $this->delegateView( [ 'viewParameters' => [ 'form' => $this->createForm( BatchType::class, [], [ 'items' => $items, 'action' => $route, ] )->createView(), ], 'contentTemplate' => '@MauticLead/Batch/form.html.twig', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'leadBatch', 'route' => $route, ], ] ); } } /** * Bulk add leads to the DNC list. * * @param int $objectId * * @return JsonResponse|Response */ public function batchDncAction(Request $request, \Mautic\LeadBundle\Model\DoNotContact $doNotContact, $objectId = 0) { if ('POST' === $request->getMethod()) { /** @var LeadModel $model */ $model = $this->getModel('lead'); $data = $request->request->all()['lead_batch_dnc'] ?? []; $ids = json_decode($data['ids'], true); $entities = []; if (is_array($ids)) { $entities = $model->getEntities( [ 'filter' => [ 'force' => [ [ 'column' => 'l.id', 'expr' => 'in', 'value' => $ids, ], ], ], 'ignore_paginator' => true, ] ); } if ($count = count($entities)) { foreach ($entities as $lead) { if ($this->security->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser())) { $doNotContact->addDncForContact($lead->getId(), 'email', DoNotContact::MANUAL, $data['reason']); } } } $this->addFlashMessage( 'mautic.lead.batch_leads_affected', [ '%count%' => $count, ] ); return new JsonResponse( [ 'closeModal' => true, 'flashes' => $this->getFlashContent(), ] ); } else { $route = $this->generateUrl( 'mautic_contact_action', [ 'objectAction' => 'batchDnc', ] ); return $this->delegateView( [ 'viewParameters' => [ 'form' => $this->createForm( DncType::class, [], [ 'action' => $route, ] )->createView(), ], 'contentTemplate' => '@MauticLead/Batch/form.html.twig', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'leadBatch', 'route' => $route, ], ] ); } } /** * Bulk edit lead stages. * * @param int $objectId * * @return JsonResponse|Response */ public function batchStagesAction(Request $request, $objectId = 0) { if ('POST' === $request->getMethod()) { /** @var LeadModel $model */ $model = $this->getModel('lead'); $data = $request->request->all()['lead_batch_stage'] ?? []; $ids = json_decode($data['ids'], true); $entities = []; if (is_array($ids)) { $entities = $model->getEntities( [ 'filter' => [ 'force' => [ [ 'column' => 'l.id', 'expr' => 'in', 'value' => $ids, ], ], ], 'ignore_paginator' => true, ] ); } $count = 0; foreach ($entities as $lead) { if ($this->security->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser())) { ++$count; if (!empty($data['addstage'])) { $stageModel = $this->getModel('stage'); $stage = $stageModel->getEntity((int) $data['addstage']); $model->addToStages($lead, $stage); } if (!empty($data['removestage'])) { $stage = $stageModel->getEntity($data['removestage']); $model->removeFromStages($lead, $stage); } } } // Save entities $model->saveEntities($entities); $this->addFlashMessage( 'mautic.lead.batch_leads_affected', [ '%count%' => $count, ] ); return new JsonResponse( [ 'closeModal' => true, 'flashes' => $this->getFlashContent(), ] ); } else { // Get a list of lists /** @var \Mautic\StageBundle\Model\StageModel $model */ $model = $this->getModel('stage'); $stages = $model->getUserStages(); $items = []; foreach ($stages as $stage) { $items[$stage['name']] = $stage['id']; } $route = $this->generateUrl( 'mautic_contact_action', [ 'objectAction' => 'batchStages', ] ); return $this->delegateView( [ 'viewParameters' => [ 'form' => $this->createForm( StageType::class, [], [ 'items' => $items, 'action' => $route, ] )->createView(), ], 'contentTemplate' => '@MauticLead/Batch/form.html.twig', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'leadBatch', 'route' => $route, ], ] ); } } /** * Bulk edit lead owner. * * @param int $objectId * * @return JsonResponse|Response */ public function batchOwnersAction(Request $request, $objectId = 0) { if (!$this->security->isGranted('user:users:view')) { return $this->accessDenied(); } if ('POST' == $request->getMethod()) { /** @var LeadModel $model */ $model = $this->getModel('lead'); $data = $request->request->all()['lead_batch_owner'] ?? []; $ids = json_decode($data['ids'], true); $entities = []; if (is_array($ids)) { $entities = $model->getEntities( [ 'filter' => [ 'force' => [ [ 'column' => 'l.id', 'expr' => 'in', 'value' => $ids, ], ], ], 'ignore_paginator' => true, ] ); } $count = 0; foreach ($entities as $lead) { if ($this->security->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser())) { ++$count; if (!empty($data['addowner'])) { $userModel = $this->getModel('user'); $user = $userModel->getEntity((int) $data['addowner']); $lead->setOwner($user); } } } // Save entities $model->saveEntities($entities); $this->addFlashMessage( 'mautic.lead.batch_leads_affected', [ '%count%' => $count, ] ); return new JsonResponse( [ 'closeModal' => true, 'flashes' => $this->getFlashContent(), ] ); } else { $userModel = $this->getModel('user.user'); \assert($userModel instanceof UserModel); $users = $userModel->getRepository()->getUserList('', 0); $items = []; foreach ($users as $user) { $items[$user['firstName'].' '.$user['lastName']] = $user['id']; } $route = $this->generateUrl( 'mautic_contact_action', [ 'objectAction' => 'batchOwners', ] ); return $this->delegateView( [ 'viewParameters' => [ 'form' => $this->createForm( OwnerType::class, [], [ 'items' => $items, 'action' => $route, ] )->createView(), ], 'contentTemplate' => '@MauticLead/Batch/form.html.twig', 'passthroughVars' => [ 'activeLink' => '#mautic_contact_index', 'mauticContent' => 'leadBatch', 'route' => $route, ], ] ); } } /** * Bulk export contacts. */ public function batchExportAction(Request $request, ExportHelper $exportHelper, EventDispatcherInterface $dispatcher): Response { // set some permissions $permissions = $this->security->isGranted( [ 'lead:leads:viewown', 'lead:leads:viewother', 'lead:leads:create', 'lead:leads:editown', 'lead:leads:editother', 'lead:leads:deleteown', 'lead:leads:deleteother', ], 'RETURN_ARRAY' ); if (!$permissions['lead:leads:viewown'] && !$permissions['lead:leads:viewother']) { return $this->accessDenied(); } elseif (!$this->security->isAdmin() && !$this->security->isGranted('lead:export:enable', 'MATCH_ONE')) { return $this->accessDenied(); } $fileType = $request->get('filetype', 'csv'); if ('csv' === $fileType && $this->coreParametersHelper->get('contact_export_in_background', false)) { return $this->contactExportCSVScheduler($dispatcher, $permissions); } /** @var LeadModel $model */ $model = $this->getModel('lead'); $session = $request->getSession(); $search = $session->get('mautic.lead.filter', ''); $orderBy = $session->get('mautic.lead.orderby', 'l.last_active'); // Add an id field to orderBy. Prevent Null-value ordering $orderById = 'l.id' !== $orderBy ? ', l.id' : ''; $orderBy = $orderBy.$orderById; $orderByDir = $session->get('mautic.lead.orderbydir', 'DESC'); $ids = $request->get('ids'); $filter = ['string' => $search, 'force' => '']; $translator = $this->translator; $anonymous = $translator->trans('mautic.lead.lead.searchcommand.isanonymous'); $mine = $translator->trans('mautic.core.searchcommand.ismine'); $indexMode = $session->get('mautic.lead.indexmode', 'list'); if (!empty($ids)) { $filter['force'] = [ [ 'column' => 'l.id', 'expr' => 'in', 'value' => json_decode($ids, true), ], ]; } else { if ('list' != $indexMode || ('list' == $indexMode && !str_contains($search, $anonymous))) { // remove anonymous leads unless requested to prevent clutter $filter['force'] .= " !$anonymous"; } if (!$permissions['lead:leads:viewother']) { $filter['force'] .= " $mine"; } } $args = [ 'start' => 0, 'limit' => 200, 'filter' => $filter, 'orderBy' => $orderBy, 'orderByDir' => $orderByDir, 'withTotalCount' => true, ]; $iterator = new IteratorExportDataModel($model, $args, fn ($contact) => $exportHelper->parseLeadToExport($contact)); return $this->exportResultsAs($iterator, $fileType, 'contacts', $exportHelper); } /** * @return array|JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\StreamedResponse */ public function contactExportAction(Request $request, ExportHelper $exportHelper, $contactId) { // set some permissions $permissions = $this->security->isGranted( [ 'lead:leads:viewown', 'lead:leads:viewother', ], 'RETURN_ARRAY' ); if (!$permissions['lead:leads:viewown'] && !$permissions['lead:leads:viewother']) { return $this->accessDenied(); } elseif (!$this->security->isAdmin() && !$this->security->isGranted('lead:export:enable', 'MATCH_ONE')) { return $this->accessDenied(); } /** @var LeadModel $leadModel */ $leadModel = $this->getModel('lead.lead'); $lead = $leadModel->getEntity($contactId); $dataType = $request->get('filetype', 'csv'); if (empty($lead)) { return $this->notFound(); } $contactFields = $lead->getProfileFields(); $export = []; foreach ($contactFields as $alias => $contactField) { $export[] = [ 'alias' => $alias, 'value' => $contactField, ]; } return $this->exportResultsAs($export, $dataType, 'contact_data_'.($contactFields['email'] ?: $contactFields['id']), $exportHelper); } public function downloadExportAction(string $fileName = ''): Response { $permissions = $this->security ->isGranted(['lead:leads:viewown', 'lead:leads:viewother'], 'RETURN_ARRAY'); if (!$permissions['lead:leads:viewown'] && !$permissions['lead:leads:viewother']) { return $this->accessDenied(); } /** @var ContactExportSchedulerModel $model */ $model = $this->getModel('lead.export_scheduler'); try { return $model->getExportFileToDownload($fileName); } catch (FileNotFoundException) { return $this->notFound(); } } /** * @param array $permissions */ private function contactExportCSVScheduler(EventDispatcherInterface $dispatcher, array $permissions): Response { /** @var ContactExportSchedulerModel $model */ $model = $this->getModel('lead.export_scheduler'); $data = $model->prepareData($permissions); $contactExportScheduler = $model->saveEntity($data); $dispatcher->dispatch( new ContactExportSchedulerEvent($contactExportScheduler), LeadEvents::POST_CONTACT_EXPORT_SCHEDULED ); $this->addFlashMessage('mautic.lead.export.being.prepared', ['%user_email%' => $this->user->getEmail()]); $response['message'] = 'Contact export scheduled for CSV file type.'; $response['flashes'] = $this->getFlashContent(); return new JsonResponse($response); } /** * Loads a specific lead statistic info. * * @return JsonResponse|Response */ public function contactStatsAction(int $objectId) { /** @var LeadModel $model */ $model = $this->getModel('lead.lead'); /** @var Lead $lead */ $lead = $model->getEntity($objectId); if (!$this->security->hasEntityAccess( 'lead:leads:viewown', 'lead:leads:viewother', $lead->getPermissionUser() ) ) { return $this->accessDenied(); } return $this->delegateView( [ 'viewParameters' => [ 'emailStats' => $model->getLeadEmailStats($lead), ], 'contentTemplate' => '@MauticLead/Lead/lead_stats.html.twig', ] ); } public function contactGroupPointsAction( Request $request, LeadModel $model, PointGroupModel $pointGroupModel, IpLookupHelper $ipLookupHelper, int $objectId): Response { $lead = $model->getEntity($objectId); if (null === $lead || !$this->security->hasEntityAccess( 'lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser() ) ) { return $this->accessDenied(); } $pointGroups = $pointGroupModel->getEntities(); $fields = []; foreach ($pointGroups as $group) { $fields[] = ContactGroupPointsType::getFieldKey($group->getId()); } $initData = []; foreach ($lead->getGroupScores() as $score) { $initData[ContactGroupPointsType::getFieldKey($score->getGroup()->getId())] = $score->getScore(); } $form = $this->formFactory->create(ContactGroupPointsType::class, $initData, [ 'action' => $this->generateUrl('mautic_contact_action', [ 'objectAction' => 'contactGroupPoints', 'objectId' => $objectId, ]), 'point_groups' => $pointGroups, ]); if (Request::METHOD_POST === $request->getMethod()) { if (!$this->isFormCancelled($form)) { if ($this->isFormValid($form)) { $leadUpdated = false; $postData = $form->getData(); foreach ($pointGroups as $group) { $scoreKey = ContactGroupPointsType::getFieldKey($group->getId()); $oldScore = $initData[$scoreKey] ?? null; $newScore = $postData[$scoreKey]; if (!is_null($oldScore) && is_null($newScore)) { // set 0 when the new score is not present, but the record exists $newScore = 0; } if (!is_null($newScore) && $newScore !== $oldScore) { $pointGroupModel->adjustPoints($lead, $group, $newScore, Lead::POINTS_SET); $delta = $newScore - ($oldScore ?? 0); // add a lead point change log $log = new PointsChangeLog(); $log->setDelta($delta); $log->setLead($lead); $log->setType('manual'); $log->setEventName($this->translator->trans('mautic.point.event.manual_change')); $log->setActionName(''); $log->setIpAddress($ipLookupHelper->getIpAddress()); $log->setDateAdded(new \DateTime()); $log->setGroup($group); $lead->addPointsChangeLog($log); $leadUpdated = true; } } if ($leadUpdated) { $model->saveEntity($lead); } return $this->postActionRedirect( [ 'returnUrl' => $this->generateUrl('mautic_contact_action', [ 'objectId' => $lead->getId(), 'objectAction' => 'view', ]), 'viewParameters' => [ 'objectId' => $lead->getId(), 'objectAction' => 'view', ], 'contentTemplate' => 'Mautic\LeadBundle\Controller\LeadController::viewAction', 'passthroughVars' => [ 'closeModal' => 1, ], ] ); } } } return $this->delegateView( [ 'viewParameters' => array_merge( [ 'fields' => $fields, 'form' => $form->createView(), 'lead' => $lead, ], ), 'contentTemplate' => '@MauticLead/Lead/group_points.html.twig', ] ); } }