Issues (3627)

bundles/LeadBundle/Controller/LeadController.php (1 issue)

1
<?php
2
3
/*
4
 * @copyright   2014 Mautic Contributors. All rights reserved
5
 * @author      Mautic
6
 *
7
 * @link        http://mautic.org
8
 *
9
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
10
 */
11
12
namespace Mautic\LeadBundle\Controller;
13
14
use Doctrine\Common\Collections\ArrayCollection;
15
use Mautic\CoreBundle\Controller\FormController;
16
use Mautic\CoreBundle\Helper\EmojiHelper;
17
use Mautic\CoreBundle\Model\IteratorExportDataModel;
18
use Mautic\LeadBundle\DataObject\LeadManipulator;
19
use Mautic\LeadBundle\Deduplicate\ContactMerger;
20
use Mautic\LeadBundle\Deduplicate\Exception\SameContactException;
21
use Mautic\LeadBundle\Entity\DoNotContact;
22
use Mautic\LeadBundle\Entity\DoNotContactRepository;
23
use Mautic\LeadBundle\Entity\Lead;
24
use Mautic\LeadBundle\Form\Type\BatchType;
25
use Mautic\LeadBundle\Form\Type\DncType;
26
use Mautic\LeadBundle\Form\Type\EmailType;
27
use Mautic\LeadBundle\Form\Type\MergeType;
28
use Mautic\LeadBundle\Form\Type\OwnerType;
29
use Mautic\LeadBundle\Form\Type\StageType;
30
use Mautic\LeadBundle\Model\LeadModel;
31
use Symfony\Component\Form\FormError;
32
use Symfony\Component\HttpFoundation\File\UploadedFile;
33
use Symfony\Component\HttpFoundation\JsonResponse;
34
use Symfony\Component\HttpFoundation\Response;
35
36
class LeadController extends FormController
37
{
38
    use LeadDetailsTrait;
39
    use FrequencyRuleTrait;
40
41
    /**
42
     * @param int $page
43
     *
44
     * @return JsonResponse|\Symfony\Component\HttpFoundation\Response
45
     */
46
    public function indexAction($page = 1)
47
    {
48
        //set some permissions
49
        $permissions = $this->get('mautic.security')->isGranted(
50
            [
51
                'lead:leads:viewown',
52
                'lead:leads:viewother',
53
                'lead:leads:create',
54
                'lead:leads:editown',
55
                'lead:leads:editother',
56
                'lead:leads:deleteown',
57
                'lead:leads:deleteother',
58
                'lead:imports:view',
59
                'lead:imports:create',
60
            ],
61
            'RETURN_ARRAY'
62
        );
63
64
        if (!$permissions['lead:leads:viewown'] && !$permissions['lead:leads:viewother']) {
65
            return $this->accessDenied();
66
        }
67
68
        $this->setListFilters();
69
70
        /** @var \Mautic\LeadBundle\Model\LeadModel $model */
71
        $model   = $this->getModel('lead');
72
        $session = $this->get('session');
73
        //set limits
74
        $limit = $session->get('mautic.lead.limit', $this->get('mautic.helper.core_parameters')->get('default_pagelimit'));
75
        $start = (1 === $page) ? 0 : (($page - 1) * $limit);
76
        if ($start < 0) {
77
            $start = 0;
78
        }
79
80
        $search = $this->request->get('search', $session->get('mautic.lead.filter', ''));
81
        $session->set('mautic.lead.filter', $search);
82
83
        //do some default filtering
84
        $orderBy    = $session->get('mautic.lead.orderby', 'l.last_active');
85
        $orderByDir = $session->get('mautic.lead.orderbydir', 'DESC');
86
87
        $filter      = ['string' => $search, 'force' => ''];
88
        $translator  = $this->get('translator');
89
        $anonymous   = $translator->trans('mautic.lead.lead.searchcommand.isanonymous');
90
        $listCommand = $translator->trans('mautic.lead.lead.searchcommand.list');
91
        $mine        = $translator->trans('mautic.core.searchcommand.ismine');
92
        $indexMode   = $this->request->get('view', $session->get('mautic.lead.indexmode', 'list'));
93
94
        $session->set('mautic.lead.indexmode', $indexMode);
95
96
        $anonymousShowing = false;
97
        if ('list' != $indexMode || ('list' == $indexMode && false === strpos($search, $anonymous))) {
98
            //remove anonymous leads unless requested to prevent clutter
99
            $filter['force'] .= " !$anonymous";
100
        } elseif (false !== strpos($search, $anonymous) && false === strpos($search, '!'.$anonymous)) {
101
            $anonymousShowing = true;
102
        }
103
104
        if (!$permissions['lead:leads:viewother']) {
105
            $filter['force'] .= " $mine";
106
        }
107
108
        $results = $model->getEntities([
109
            'start'          => $start,
110
            'limit'          => $limit,
111
            'filter'         => $filter,
112
            'orderBy'        => $orderBy,
113
            'orderByDir'     => $orderByDir,
114
            'withTotalCount' => true,
115
        ]);
116
117
        $count = $results['count'];
118
        unset($results['count']);
119
120
        $leads = $results['results'];
121
        unset($results);
122
123
        if ($count && $count < ($start + 1)) {
124
            //the number of entities are now less then the current page so redirect to the last page
125
            if (1 === $count) {
126
                $lastPage = 1;
127
            } else {
128
                $lastPage = (ceil($count / $limit)) ?: 1;
129
            }
130
            $session->set('mautic.lead.page', $lastPage);
131
            $returnUrl = $this->generateUrl('mautic_contact_index', ['page' => $lastPage]);
132
133
            return $this->postActionRedirect(
134
                [
135
                    'returnUrl'       => $returnUrl,
136
                    'viewParameters'  => ['page' => $lastPage],
137
                    'contentTemplate' => 'MauticLeadBundle:Lead:index',
138
                    'passthroughVars' => [
139
                        'activeLink'    => '#mautic_contact_index',
140
                        'mauticContent' => 'lead',
141
                    ],
142
                ]
143
            );
144
        }
145
146
        //set what page currently on so that we can return here after form submission/cancellation
147
        $session->set('mautic.lead.page', $page);
148
149
        $tmpl = $this->request->isXmlHttpRequest() ? $this->request->get('tmpl', 'index') : 'index';
150
151
        $listArgs = [];
152
        if (!$this->get('mautic.security')->isGranted('lead:lists:viewother')) {
153
            $listArgs['filter']['force'] = " $mine";
154
        }
155
156
        $lists = $this->getModel('lead.list')->getUserLists();
157
158
        //check to see if in a single list
159
        $inSingleList = (1 === substr_count($search, "$listCommand:")) ? true : false;
160
        $list         = [];
161
        if ($inSingleList) {
162
            preg_match("/$listCommand:(.*?)(?=\s|$)/", $search, $matches);
163
164
            if (!empty($matches[1])) {
165
                $alias = $matches[1];
166
                foreach ($lists as $l) {
167
                    if ($alias === $l['alias']) {
168
                        $list = $l;
169
                        break;
170
                    }
171
                }
172
            }
173
        }
174
175
        // Get the max ID of the latest lead added
176
        $maxLeadId = $model->getRepository()->getMaxLeadId();
177
178
        /** @var DoNotContactRepository $dncRepository */
179
        $dncRepository = $this->getModel('lead.dnc')->getDncRepo();
180
181
        return $this->delegateView(
182
            [
183
                'viewParameters' => [
184
                    'searchValue'      => $search,
185
                    'columns'          => $this->get('mautic.lead.columns.dictionary')->getColumns(),
186
                    'items'            => $leads,
187
                    'page'             => $page,
188
                    'totalItems'       => $count,
189
                    'limit'            => $limit,
190
                    'permissions'      => $permissions,
191
                    'tmpl'             => $tmpl,
192
                    'indexMode'        => $indexMode,
193
                    'lists'            => $lists,
194
                    'currentList'      => $list,
195
                    'security'         => $this->get('mautic.security'),
196
                    'inSingleList'     => $inSingleList,
197
                    'noContactList'    => $dncRepository->getChannelList(null, array_keys($leads)),
198
                    'maxLeadId'        => $maxLeadId,
199
                    'anonymousShowing' => $anonymousShowing,
200
                ],
201
                'contentTemplate' => "MauticLeadBundle:Lead:{$indexMode}.html.php",
202
                'passthroughVars' => [
203
                    'activeLink'    => '#mautic_contact_index',
204
                    'mauticContent' => 'lead',
205
                    'route'         => $this->generateUrl('mautic_contact_index', ['page' => $page]),
206
                ],
207
            ]
208
        );
209
    }
210
211
    /**
212
     * @return JsonResponse|Response
213
     */
214
    public function quickAddAction()
215
    {
216
        /** @var \Mautic\LeadBundle\Model\LeadModel $model */
217
        $model = $this->getModel('lead.lead');
218
219
        // Get the quick add form
220
        $action = $this->generateUrl('mautic_contact_action', ['objectAction' => 'new', 'qf' => 1]);
221
222
        $fields = $this->getModel('lead.field')->getEntities(
223
            [
224
                'filter' => [
225
                    'force' => [
226
                        [
227
                            'column' => 'f.isPublished',
228
                            'expr'   => 'eq',
229
                            'value'  => true,
230
                        ],
231
                        [
232
                            'column' => 'f.isShortVisible',
233
                            'expr'   => 'eq',
234
                            'value'  => true,
235
                        ],
236
                        [
237
                            'column' => 'f.object',
238
                            'expr'   => 'like',
239
                            'value'  => 'lead',
240
                        ],
241
                    ],
242
                ],
243
                'hydration_mode' => 'HYDRATE_ARRAY',
244
            ]
245
        );
246
247
        $quickForm = $model->createForm($model->getEntity(), $this->get('form.factory'), $action, ['fields' => $fields, 'isShortForm' => true]);
248
249
        //set the default owner to the currently logged in user
250
        $currentUser = $this->get('security.token_storage')->getToken()->getUser();
251
        $quickForm->get('owner')->setData($currentUser);
252
253
        return $this->delegateView(
254
            [
255
                'viewParameters' => [
256
                    'quickForm' => $quickForm->createView(),
257
                ],
258
                'contentTemplate' => 'MauticLeadBundle:Lead:quickadd.html.php',
259
                'passthroughVars' => [
260
                    'activeLink'    => '#mautic_contact_index',
261
                    'mauticContent' => 'lead',
262
                    'route'         => false,
263
                ],
264
            ]
265
        );
266
    }
267
268
    /**
269
     * Loads a specific lead into the detailed panel.
270
     *
271
     * @param $objectId
272
     *
273
     * @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\Response
274
     */
275
    public function viewAction($objectId)
276
    {
277
        /** @var \Mautic\LeadBundle\Model\LeadModel $model */
278
        $model = $this->getModel('lead.lead');
279
280
        // When we change company data these changes get cached
281
        // so we need to clear the entity manager
282
        $model->getRepository()->clear();
283
284
        /** @var \Mautic\LeadBundle\Entity\Lead $lead */
285
        $lead = $model->getEntity($objectId);
286
287
        //set some permissions
288
        $permissions = $this->get('mautic.security')->isGranted(
289
            [
290
                'lead:leads:viewown',
291
                'lead:leads:viewother',
292
                'lead:leads:create',
293
                'lead:leads:editown',
294
                'lead:leads:editother',
295
                'lead:leads:deleteown',
296
                'lead:leads:deleteother',
297
            ],
298
            'RETURN_ARRAY'
299
        );
300
301
        if (null === $lead) {
302
            //get the page we came from
303
            $page = $this->get('session')->get('mautic.lead.page', 1);
304
305
            //set the return URL
306
            $returnUrl = $this->generateUrl('mautic_contact_index', ['page' => $page]);
307
308
            return $this->postActionRedirect(
309
                [
310
                    'returnUrl'       => $returnUrl,
311
                    'viewParameters'  => ['page' => $page],
312
                    'contentTemplate' => 'MauticLeadBundle:Lead:index',
313
                    'passthroughVars' => [
314
                        'activeLink'    => '#mautic_contact_index',
315
                        'mauticContent' => 'contact',
316
                    ],
317
                    'flashes' => [
318
                        [
319
                            'type'    => 'error',
320
                            'msg'     => 'mautic.lead.lead.error.notfound',
321
                            'msgVars' => ['%id%' => $objectId],
322
                        ],
323
                    ],
324
                ]
325
            );
326
        }
327
328
        if (!$this->get('mautic.security')->hasEntityAccess(
329
            'lead:leads:viewown',
330
            'lead:leads:viewother',
331
            $lead->getPermissionUser()
332
        )
333
        ) {
334
            return $this->accessDenied();
335
        }
336
337
        $fields            = $lead->getFields();
338
        $integrationHelper = $this->get('mautic.helper.integration');
339
        $socialProfiles    = (array) $integrationHelper->getUserProfiles($lead, $fields);
340
        $socialProfileUrls = $integrationHelper->getSocialProfileUrlRegex(false);
341
        /* @var \Mautic\LeadBundle\Model\CompanyModel $model */
342
343
        $companyModel = $this->getModel('lead.company');
344
345
        $companiesRepo = $companyModel->getRepository();
346
        $companies     = $companiesRepo->getCompaniesByLeadId($objectId);
347
        // Set the social profile templates
348
        if ($socialProfiles) {
349
            foreach ($socialProfiles as $integration => &$details) {
350
                if ($integrationObject = $integrationHelper->getIntegrationObject($integration)) {
351
                    if ($template = $integrationObject->getSocialProfileTemplate()) {
352
                        $details['social_profile_template'] = $template;
353
                    }
354
                }
355
356
                if (!isset($details['social_profile_template'])) {
357
                    // No profile template found
358
                    unset($socialProfiles[$integration]);
359
                }
360
            }
361
        }
362
363
        // We need the DoNotContact repository to check if a lead is flagged as do not contact
364
        $dnc = $this->getDoctrine()->getManager()->getRepository('MauticLeadBundle:DoNotContact')->getEntriesByLeadAndChannel($lead, 'email');
365
366
        $dncSms = $this->getDoctrine()->getManager()->getRepository('MauticLeadBundle:DoNotContact')->getEntriesByLeadAndChannel($lead, 'sms');
367
368
        $integrationRepo = $this->get('doctrine.orm.entity_manager')->getRepository('MauticPluginBundle:IntegrationEntity');
369
370
        return $this->delegateView(
371
            [
372
                'viewParameters' => [
373
                    'lead'              => $lead,
374
                    'avatarPanelState'  => $this->request->cookies->get('mautic_lead_avatar_panel', 'expanded'),
375
                    'fields'            => $fields,
376
                    'companies'         => $companies,
377
                    'socialProfiles'    => $socialProfiles,
378
                    'socialProfileUrls' => $socialProfileUrls,
379
                    'places'            => $this->getPlaces($lead),
380
                    'permissions'       => $permissions,
381
                    'events'            => $this->getEngagements($lead),
382
                    'upcomingEvents'    => $this->getScheduledCampaignEvents($lead),
383
                    'engagementData'    => $this->getEngagementData($lead),
384
                    'noteCount'         => $this->getModel('lead.note')->getNoteCount($lead, true),
385
                    'integrations'      => $integrationRepo->getIntegrationEntityByLead($lead->getId()),
386
                    'devices'           => $this->get('mautic.lead.repository.lead_device')->getLeadDevices($lead),
387
                    'auditlog'          => $this->getAuditlogs($lead),
388
                    'doNotContact'      => end($dnc),
389
                    'doNotContactSms'   => end($dncSms),
390
                    'leadNotes'         => $this->forward(
391
                        'MauticLeadBundle:Note:index',
392
                        [
393
                            'leadId'     => $lead->getId(),
394
                            'ignoreAjax' => 1,
395
                        ]
396
                    )->getContent(),
397
                ],
398
                'contentTemplate' => 'MauticLeadBundle:Lead:lead.html.php',
399
                'passthroughVars' => [
400
                    'activeLink'    => '#mautic_contact_index',
401
                    'mauticContent' => 'lead',
402
                    'route'         => $this->generateUrl(
403
                        'mautic_contact_action',
404
                        [
405
                            'objectAction' => 'view',
406
                            'objectId'     => $lead->getId(),
407
                        ]
408
                    ),
409
                ],
410
            ]
411
        );
412
    }
413
414
    /**
415
     * Generates new form and processes post data.
416
     *
417
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
418
     */
419
    public function newAction()
420
    {
421
        /** @var LeadModel $model */
422
        $model = $this->getModel('lead.lead');
423
        $lead  = $model->getEntity();
424
425
        if (!$this->get('mautic.security')->isGranted('lead:leads:create')) {
426
            return $this->accessDenied();
427
        }
428
429
        //set the page we came from
430
        $page   = $this->get('session')->get('mautic.lead.page', 1);
431
        $action = $this->generateUrl('mautic_contact_action', ['objectAction' => 'new']);
432
        $fields = $this->getModel('lead.field')->getPublishedFieldArrays('lead');
433
        $form   = $model->createForm($lead, $this->get('form.factory'), $action, ['fields' => $fields]);
434
435
        ///Check for a submitted form and process it
436
        if ('POST' === $this->request->getMethod()) {
437
            $valid = false;
438
            if (!$cancelled = $this->isFormCancelled($form)) {
439
                if ($valid = $this->isFormValid($form)) {
440
                    //get custom field values
441
                    $data = $this->request->request->get('lead');
442
443
                    //pull the data from the form in order to apply the form's formatting
444
                    foreach ($form as $f) {
445
                        if ('companies' !== $f->getName()) {
446
                            $data[$f->getName()] = $f->getData();
447
                        }
448
                    }
449
450
                    $companies = [];
451
                    if (isset($data['companies'])) {
452
                        $companies = $data['companies'];
453
                        unset($data['companies']);
454
                    }
455
456
                    $model->setFieldValues($lead, $data, true);
457
458
                    //form is valid so process the data
459
                    $lead->setManipulator(new LeadManipulator(
460
                        'lead',
461
                        'lead',
462
                        null,
463
                        $this->get('mautic.helper.user')->getUser()->getName()
464
                    ));
465
466
                    /** @var LeadRepository $contactRepository */
467
                    $contactRepository = $this->getDoctrine()->getManager()->getRepository(Lead::class);
468
469
                    // Save here as we need the entity with an ID for the company code bellow.
470
                    $contactRepository->saveEntity($lead);
471
472
                    if (!empty($companies)) {
473
                        $model->modifyCompanies($lead, $companies);
474
                    }
475
476
                    // Save here through the model to trigger all subscribers.
477
                    $model->saveEntity($lead);
478
479
                    // Upload avatar if applicable
480
                    $image = $form['preferred_profile_image']->getData();
481
                    if ('custom' === $image) {
482
                        // Check for a file
483
                        if ($form['custom_avatar']->getData()) {
484
                            $this->uploadAvatar($lead);
485
                        }
486
                    }
487
488
                    $identifier = $this->get('translator')->trans($lead->getPrimaryIdentifier());
489
490
                    $this->addFlash(
491
                        'mautic.core.notice.created',
492
                        [
493
                            '%name%'      => $identifier,
494
                            '%menu_link%' => 'mautic_contact_index',
495
                            '%url%'       => $this->generateUrl(
496
                                'mautic_contact_action',
497
                                [
498
                                    'objectAction' => 'edit',
499
                                    'objectId'     => $lead->getId(),
500
                                ]
501
                            ),
502
                        ]
503
                    );
504
505
                    $inQuickForm = $this->request->get('qf', false);
506
507
                    if ($inQuickForm) {
508
                        $viewParameters = ['page' => $page];
509
                        $returnUrl      = $this->generateUrl('mautic_contact_index', $viewParameters);
510
                        $template       = 'MauticLeadBundle:Lead:index';
511
                    } elseif ($form->get('buttons')->get('save')->isClicked()) {
0 ignored issues
show
The method isClicked() does not exist on Symfony\Component\Form\Form. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

511
                    } elseif ($form->get('buttons')->get('save')->/** @scrutinizer ignore-call */ isClicked()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
512
                        $viewParameters = [
513
                            'objectAction' => 'view',
514
                            'objectId'     => $lead->getId(),
515
                        ];
516
                        $returnUrl = $this->generateUrl('mautic_contact_action', $viewParameters);
517
                        $template  = 'MauticLeadBundle:Lead:view';
518
                    } else {
519
                        return $this->editAction($lead->getId(), true);
520
                    }
521
                }
522
            } else {
523
                $viewParameters = ['page' => $page];
524
                $returnUrl      = $this->generateUrl('mautic_contact_index', $viewParameters);
525
                $template       = 'MauticLeadBundle:Lead:index';
526
            }
527
528
            if ($cancelled || $valid) { //cancelled or success
529
                return $this->postActionRedirect(
530
                    [
531
                        'returnUrl'       => $returnUrl,
532
                        'viewParameters'  => $viewParameters,
533
                        'contentTemplate' => $template,
534
                        'passthroughVars' => [
535
                            'activeLink'    => '#mautic_contact_index',
536
                            'mauticContent' => 'lead',
537
                            'closeModal'    => 1, //just in case in quick form
538
                        ],
539
                    ]
540
                );
541
            }
542
        } else {
543
            //set the default owner to the currently logged in user
544
            $currentUser = $this->get('security.token_storage')->getToken()->getUser();
545
            $form->get('owner')->setData($currentUser);
546
        }
547
548
        return $this->delegateView(
549
            [
550
                'viewParameters' => [
551
                    'form'   => $form->createView(),
552
                    'lead'   => $lead,
553
                    'fields' => $model->organizeFieldsByGroup($fields),
554
                ],
555
                'contentTemplate' => 'MauticLeadBundle:Lead:form.html.php',
556
                'passthroughVars' => [
557
                    'activeLink'    => '#mautic_contact_index',
558
                    'mauticContent' => 'lead',
559
                    'route'         => $this->generateUrl(
560
                        'mautic_contact_action',
561
                        [
562
                            'objectAction' => 'new',
563
                        ]
564
                    ),
565
                ],
566
            ]
567
        );
568
    }
569
570
    /**
571
     * Generates edit form.
572
     *
573
     * @param            $objectId
574
     * @param bool|false $ignorePost
575
     *
576
     * @return array|JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
577
     */
578
    public function editAction($objectId, $ignorePost = false)
579
    {
580
        /** @var LeadModel $model */
581
        $model = $this->getModel('lead.lead');
582
        $lead  = $model->getEntity($objectId);
583
584
        //set the page we came from
585
        $page = $this->get('session')->get('mautic.lead.page', 1);
586
587
        //set the return URL
588
        $returnUrl = $this->generateUrl('mautic_contact_index', ['page' => $page]);
589
590
        $postActionVars = [
591
            'returnUrl'       => $returnUrl,
592
            'viewParameters'  => ['page' => $page],
593
            'contentTemplate' => 'MauticLeadBundle:Lead:index',
594
            'passthroughVars' => [
595
                'activeLink'    => '#mautic_contact_index',
596
                'mauticContent' => 'lead',
597
            ],
598
        ];
599
        //lead not found
600
        if (null === $lead) {
601
            return $this->postActionRedirect(
602
                array_merge(
603
                    $postActionVars,
604
                    [
605
                        'flashes' => [
606
                            [
607
                                'type'    => 'error',
608
                                'msg'     => 'mautic.lead.lead.error.notfound',
609
                                'msgVars' => ['%id%' => $objectId],
610
                            ],
611
                        ],
612
                    ]
613
                )
614
            );
615
        } elseif (!$this->get('mautic.security')->hasEntityAccess(
616
            'lead:leads:editown',
617
            'lead:leads:editother',
618
            $lead->getPermissionUser()
619
        )
620
        ) {
621
            return $this->accessDenied();
622
        } elseif ($model->isLocked($lead)) {
623
            //deny access if the entity is locked
624
            return $this->isLocked($postActionVars, $lead, 'lead.lead');
625
        }
626
627
        $action = $this->generateUrl('mautic_contact_action', ['objectAction' => 'edit', 'objectId' => $objectId]);
628
        $fields = $this->getModel('lead.field')->getPublishedFieldArrays('lead');
629
        $form   = $model->createForm($lead, $this->get('form.factory'), $action, ['fields' => $fields]);
630
631
        ///Check for a submitted form and process it
632
        if (!$ignorePost && 'POST' == $this->request->getMethod()) {
633
            $valid = false;
634
            if (!$cancelled = $this->isFormCancelled($form)) {
635
                if ($valid = $this->isFormValid($form)) {
636
                    $data = $this->request->request->get('lead');
637
638
                    //pull the data from the form in order to apply the form's formatting
639
                    foreach ($form as $f) {
640
                        if (('companies' !== $f->getName()) && ('company' !== $f->getName())) {
641
                            $data[$f->getName()] = $f->getData();
642
                        }
643
                    }
644
645
                    $companies = [];
646
                    if (isset($data['companies'])) {
647
                        $companies = $data['companies'];
648
                        unset($data['companies']);
649
                    }
650
                    $model->setFieldValues($lead, $data, true);
651
652
                    //form is valid so process the data
653
                    $lead->setManipulator(new LeadManipulator(
654
                        'lead',
655
                        'lead',
656
                        $objectId,
657
                        $this->get('mautic.helper.user')->getUser()->getName()
658
                    ));
659
                    $model->saveEntity($lead, $form->get('buttons')->get('save')->isClicked());
660
                    $model->modifyCompanies($lead, $companies);
661
662
                    // Upload avatar if applicable
663
                    $image = $form['preferred_profile_image']->getData();
664
                    if ('custom' == $image) {
665
                        // Check for a file
666
                        /** @var UploadedFile $file */
667
                        if ($file = $form['custom_avatar']->getData()) {
668
                            $this->uploadAvatar($lead);
669
670
                            // Note the avatar update so that it can be forced to update
671
                            $this->get('session')->set('mautic.lead.avatar.updated', true);
672
                        }
673
                    }
674
675
                    $identifier = $this->get('translator')->trans($lead->getPrimaryIdentifier());
676
677
                    $this->addFlash(
678
                        'mautic.core.notice.updated',
679
                        [
680
                            '%name%'      => $identifier,
681
                            '%menu_link%' => 'mautic_contact_index',
682
                            '%url%'       => $this->generateUrl(
683
                                'mautic_contact_action',
684
                                [
685
                                    'objectAction' => 'edit',
686
                                    'objectId'     => $lead->getId(),
687
                                ]
688
                            ),
689
                        ]
690
                    );
691
                }
692
            } else {
693
                //unlock the entity
694
                $model->unlockEntity($lead);
695
            }
696
697
            if ($cancelled || ($valid && $form->get('buttons')->get('save')->isClicked())) {
698
                $viewParameters = [
699
                    'objectAction' => 'view',
700
                    'objectId'     => $lead->getId(),
701
                ];
702
703
                return $this->postActionRedirect(
704
                    array_merge(
705
                        $postActionVars,
706
                        [
707
                            'returnUrl'       => $this->generateUrl('mautic_contact_action', $viewParameters),
708
                            'viewParameters'  => $viewParameters,
709
                            'contentTemplate' => 'MauticLeadBundle:Lead:view',
710
                        ]
711
                    )
712
                );
713
            } elseif ($valid) {
714
                // Refetch and recreate the form in order to populate data manipulated in the entity itself
715
                $lead = $model->getEntity($objectId);
716
                $form = $model->createForm($lead, $this->get('form.factory'), $action, ['fields' => $fields]);
717
            }
718
        } else {
719
            //lock the entity
720
            $model->lockEntity($lead);
721
        }
722
723
        return $this->delegateView(
724
            [
725
                'viewParameters' => [
726
                    'form'   => $form->createView(),
727
                    'lead'   => $lead,
728
                    'fields' => $lead->getFields(), //pass in the lead fields as they are already organized by ['group']['alias']
729
                ],
730
                'contentTemplate' => 'MauticLeadBundle:Lead:form.html.php',
731
                'passthroughVars' => [
732
                    'activeLink'    => '#mautic_contact_index',
733
                    'mauticContent' => 'lead',
734
                    'route'         => $this->generateUrl(
735
                        'mautic_contact_action',
736
                        [
737
                            'objectAction' => 'edit',
738
                            'objectId'     => $lead->getId(),
739
                        ]
740
                    ),
741
                ],
742
            ]
743
        );
744
    }
745
746
    /**
747
     * Upload an asset.
748
     */
749
    private function uploadAvatar(Lead $lead)
750
    {
751
        $leadInformation = $this->request->files->get('lead', []);
752
        $file            = $leadInformation['custom_avatar'] ?? null;
753
        $avatarDir       = $this->get('mautic.helper.template.avatar')->getAvatarPath(true);
754
755
        if (!file_exists($avatarDir)) {
756
            mkdir($avatarDir);
757
        }
758
759
        $file->move($avatarDir, 'avatar'.$lead->getId());
760
761
        //remove the file from request
762
        $this->request->files->remove('lead');
763
    }
764
765
    /**
766
     * Generates merge form and action.
767
     *
768
     * @param $objectId
769
     *
770
     * @return array|JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
771
     */
772
    public function mergeAction($objectId)
773
    {
774
        /** @var \Mautic\LeadBundle\Model\LeadModel $model */
775
        $model    = $this->getModel('lead');
776
        $mainLead = $model->getEntity($objectId);
777
        $page     = $this->get('session')->get('mautic.lead.page', 1);
778
779
        //set the return URL
780
        $returnUrl = $this->generateUrl('mautic_contact_index', ['page' => $page]);
781
782
        $postActionVars = [
783
            'returnUrl'       => $returnUrl,
784
            'viewParameters'  => ['page' => $page],
785
            'contentTemplate' => 'MauticLeadBundle:Lead:index',
786
            'passthroughVars' => [
787
                'activeLink'    => '#mautic_contact_index',
788
                'mauticContent' => 'lead',
789
            ],
790
        ];
791
792
        if (null === $mainLead) {
793
            return $this->postActionRedirect(
794
                array_merge(
795
                    $postActionVars,
796
                    [
797
                        'flashes' => [
798
                            [
799
                                'type'    => 'error',
800
                                'msg'     => 'mautic.lead.lead.error.notfound',
801
                                'msgVars' => ['%id%' => $objectId],
802
                            ],
803
                        ],
804
                    ]
805
                )
806
            );
807
        }
808
809
        //do some default filtering
810
        $session = $this->get('session');
811
        $search  = $this->request->get('search', $session->get('mautic.lead.merge.filter', ''));
812
        $session->set('mautic.lead.merge.filter', $search);
813
        $leads = [];
814
815
        if (!empty($search)) {
816
            $filter = [
817
                'string' => $search,
818
                'force'  => [
819
                    [
820
                        'column' => 'l.date_identified',
821
                        'expr'   => 'isNotNull',
822
                        'value'  => $mainLead->getId(),
823
                    ],
824
                    [
825
                        'column' => 'l.id',
826
                        'expr'   => 'neq',
827
                        'value'  => $mainLead->getId(),
828
                    ],
829
                ],
830
            ];
831
832
            $leads = $model->getEntities(
833
                [
834
                    'limit'          => 25,
835
                    'filter'         => $filter,
836
                    'orderBy'        => 'l.firstname,l.lastname,l.company,l.email',
837
                    'orderByDir'     => 'ASC',
838
                    'withTotalCount' => false,
839
                ]
840
            );
841
        }
842
843
        $leadChoices = [];
844
        foreach ($leads as $l) {
845
            $leadChoices[$l->getPrimaryIdentifier()] = $l->getId();
846
        }
847
848
        $action = $this->generateUrl('mautic_contact_action', ['objectAction' => 'merge', 'objectId' => $mainLead->getId()]);
849
850
        $form = $this->get('form.factory')->create(
851
            MergeType::class,
852
            [],
853
            [
854
                'action' => $action,
855
                'leads'  => $leadChoices,
856
            ]
857
        );
858
859
        if ('POST' == $this->request->getMethod()) {
860
            $valid = true;
861
            if (!$this->isFormCancelled($form)) {
862
                if ($valid = $this->isFormValid($form)) {
863
                    $data      = $form->getData();
864
                    $secLeadId = $data['lead_to_merge'];
865
                    $secLead   = $model->getEntity($secLeadId);
866
867
                    if (null === $secLead) {
868
                        return $this->postActionRedirect(
869
                            array_merge(
870
                                $postActionVars,
871
                                [
872
                                    'flashes' => [
873
                                        [
874
                                            'type'    => 'error',
875
                                            'msg'     => 'mautic.lead.lead.error.notfound',
876
                                            'msgVars' => ['%id%' => $secLead->getId()],
877
                                        ],
878
                                    ],
879
                                ]
880
                            )
881
                        );
882
                    } elseif (
883
                        !$this->get('mautic.security')->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $mainLead->getPermissionUser())
884
                        || !$this->get('mautic.security')->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $secLead->getPermissionUser())
885
                    ) {
886
                        return $this->accessDenied();
887
                    } elseif ($model->isLocked($mainLead)) {
888
                        //deny access if the entity is locked
889
                        return $this->isLocked($postActionVars, $secLead, 'lead');
890
                    } elseif ($model->isLocked($secLead)) {
891
                        //deny access if the entity is locked
892
                        return $this->isLocked($postActionVars, $secLead, 'lead');
893
                    }
894
895
                    //Both leads are good so now we merge them
896
                    /** @var ContactMerger $contactMerger */
897
                    $contactMerger = $this->container->get('mautic.lead.merger');
898
                    try {
899
                        $mainLead = $contactMerger->merge($mainLead, $secLead);
900
                    } catch (SameContactException $exception) {
901
                    }
902
                }
903
            }
904
905
            if ($valid) {
906
                $viewParameters = [
907
                    'objectId'     => $mainLead->getId(),
908
                    'objectAction' => 'view',
909
                ];
910
911
                return $this->postActionRedirect(
912
                    [
913
                        'returnUrl'       => $this->generateUrl('mautic_contact_action', $viewParameters),
914
                        'viewParameters'  => $viewParameters,
915
                        'contentTemplate' => 'MauticLeadBundle:Lead:view',
916
                        'passthroughVars' => [
917
                            'closeModal' => 1,
918
                        ],
919
                    ]
920
                );
921
            }
922
        }
923
924
        $tmpl = $this->request->get('tmpl', 'index');
925
926
        return $this->delegateView(
927
            [
928
                'viewParameters' => [
929
                    'tmpl'         => $tmpl,
930
                    'leads'        => $leads,
931
                    'searchValue'  => $search,
932
                    'action'       => $action,
933
                    'form'         => $form->createView(),
934
                    'currentRoute' => $this->generateUrl(
935
                        'mautic_contact_action',
936
                        [
937
                            'objectAction' => 'merge',
938
                            'objectId'     => $mainLead->getId(),
939
                        ]
940
                    ),
941
                ],
942
                'contentTemplate' => 'MauticLeadBundle:Lead:merge.html.php',
943
                'passthroughVars' => [
944
                    'route'  => false,
945
                    'target' => ('update' == $tmpl) ? '.lead-merge-options' : null,
946
                ],
947
            ]
948
        );
949
    }
950
951
    /**
952
     * Generates contact frequency rules form and action.
953
     *
954
     * @param $objectId
955
     *
956
     * @return array|JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
957
     */
958
    public function contactFrequencyAction($objectId)
959
    {
960
        /** @var LeadModel $model */
961
        $model = $this->getModel('lead');
962
        $lead  = $model->getEntity($objectId);
963
964
        if (null === $lead
965
            || !$this->get('mautic.security')->hasEntityAccess(
966
                'lead:leads:editown',
967
                'lead:leads:editother',
968
                $lead->getPermissionUser()
969
            )
970
        ) {
971
            return $this->accessDenied();
972
        }
973
974
        $viewParameters = [
975
            'objectId'     => $lead->getId(),
976
            'objectAction' => 'view',
977
        ];
978
979
        $form = $this->getFrequencyRuleForm(
980
            $lead,
981
            $viewParameters,
982
            $data,
983
            false,
984
            $this->generateUrl('mautic_contact_action', ['objectAction' => 'contactFrequency', 'objectId' => $lead->getId()])
985
        );
986
987
        if (true === $form) {
988
            return $this->postActionRedirect(
989
                [
990
                    'returnUrl' => $this->generateUrl('mautic_contact_action', [
991
                        'objectId'     => $lead->getId(),
992
                        'objectAction' => 'view',
993
                    ]),
994
                    'viewParameters'  => $viewParameters,
995
                    'contentTemplate' => 'MauticLeadBundle:Lead:view',
996
                    'passthroughVars' => [
997
                        'closeModal' => 1,
998
                    ],
999
                ]
1000
            );
1001
        }
1002
1003
        $tmpl = $this->request->get('tmpl', 'index');
1004
1005
        return $this->delegateView(
1006
            [
1007
                'viewParameters' => array_merge(
1008
                    [
1009
                        'tmpl'         => $tmpl,
1010
                        'form'         => $form->createView(),
1011
                        'currentRoute' => $this->generateUrl(
1012
                            'mautic_contact_action',
1013
                            [
1014
                                'objectAction' => 'contactFrequency',
1015
                                'objectId'     => $lead->getId(),
1016
                            ]
1017
                        ),
1018
                        'lead' => $lead,
1019
                    ],
1020
                    $viewParameters
1021
                ),
1022
                'contentTemplate' => 'MauticLeadBundle:Lead:frequency.html.php',
1023
                'passthroughVars' => [
1024
                    'route'  => false,
1025
                    'target' => ('update' == $tmpl) ? '.lead-frequency-options' : null,
1026
                ],
1027
            ]
1028
        );
1029
    }
1030
1031
    /**
1032
     * Deletes the entity.
1033
     *
1034
     * @param $objectId
1035
     *
1036
     * @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse
1037
     */
1038
    public function deleteAction($objectId)
1039
    {
1040
        $page      = $this->get('session')->get('mautic.lead.page', 1);
1041
        $returnUrl = $this->generateUrl('mautic_contact_index', ['page' => $page]);
1042
        $flashes   = [];
1043
1044
        $postActionVars = [
1045
            'returnUrl'       => $returnUrl,
1046
            'viewParameters'  => ['page' => $page],
1047
            'contentTemplate' => 'MauticLeadBundle:Lead:index',
1048
            'passthroughVars' => [
1049
                'activeLink'    => '#mautic_contact_index',
1050
                'mauticContent' => 'lead',
1051
            ],
1052
        ];
1053
1054
        if ('POST' == $this->request->getMethod()) {
1055
            $model  = $this->getModel('lead.lead');
1056
            $entity = $model->getEntity($objectId);
1057
1058
            if (null === $entity) {
1059
                $flashes[] = [
1060
                    'type'    => 'error',
1061
                    'msg'     => 'mautic.lead.lead.error.notfound',
1062
                    'msgVars' => ['%id%' => $objectId],
1063
                ];
1064
            } elseif (!$this->get('mautic.security')->hasEntityAccess(
1065
                'lead:leads:deleteown',
1066
                'lead:leads:deleteother',
1067
                $entity->getPermissionUser()
1068
            )
1069
            ) {
1070
                return $this->accessDenied();
1071
            } elseif ($model->isLocked($entity)) {
1072
                return $this->isLocked($postActionVars, $entity, 'lead.lead');
1073
            } else {
1074
                $model->deleteEntity($entity);
1075
1076
                $identifier = $this->get('translator')->trans($entity->getPrimaryIdentifier());
1077
                $flashes[]  = [
1078
                    'type'    => 'notice',
1079
                    'msg'     => 'mautic.core.notice.deleted',
1080
                    'msgVars' => [
1081
                        '%name%' => $identifier,
1082
                        '%id%'   => $objectId,
1083
                    ],
1084
                ];
1085
            }
1086
        } //else don't do anything
1087
1088
        return $this->postActionRedirect(
1089
            array_merge(
1090
                $postActionVars,
1091
                [
1092
                    'flashes' => $flashes,
1093
                ]
1094
            )
1095
        );
1096
    }
1097
1098
    /**
1099
     * Deletes a group of entities.
1100
     *
1101
     * @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse
1102
     */
1103
    public function batchDeleteAction()
1104
    {
1105
        $page      = $this->get('session')->get('mautic.lead.page', 1);
1106
        $returnUrl = $this->generateUrl('mautic_contact_index', ['page' => $page]);
1107
        $flashes   = [];
1108
1109
        $postActionVars = [
1110
            'returnUrl'       => $returnUrl,
1111
            'viewParameters'  => ['page' => $page],
1112
            'contentTemplate' => 'MauticLeadBundle:Lead:index',
1113
            'passthroughVars' => [
1114
                'activeLink'    => '#mautic_contact_index',
1115
                'mauticContent' => 'lead',
1116
            ],
1117
        ];
1118
1119
        if ('POST' == $this->request->getMethod()) {
1120
            $model     = $this->getModel('lead');
1121
            $ids       = json_decode($this->request->query->get('ids', '{}'));
1122
            $deleteIds = [];
1123
1124
            // Loop over the IDs to perform access checks pre-delete
1125
            foreach ($ids as $objectId) {
1126
                $entity = $model->getEntity($objectId);
1127
1128
                if (null === $entity) {
1129
                    $flashes[] = [
1130
                        'type'    => 'error',
1131
                        'msg'     => 'mautic.lead.lead.error.notfound',
1132
                        'msgVars' => ['%id%' => $objectId],
1133
                    ];
1134
                } elseif (!$this->get('mautic.security')->hasEntityAccess(
1135
                    'lead:leads:deleteown',
1136
                    'lead:leads:deleteother',
1137
                    $entity->getPermissionUser()
1138
                )
1139
                ) {
1140
                    $flashes[] = $this->accessDenied(true);
1141
                } elseif ($model->isLocked($entity)) {
1142
                    $flashes[] = $this->isLocked($postActionVars, $entity, 'lead', true);
1143
                } else {
1144
                    $deleteIds[] = $objectId;
1145
                }
1146
            }
1147
1148
            // Delete everything we are able to
1149
            if (!empty($deleteIds)) {
1150
                $entities = $model->deleteEntities($deleteIds);
1151
1152
                $flashes[] = [
1153
                    'type'    => 'notice',
1154
                    'msg'     => 'mautic.lead.lead.notice.batch_deleted',
1155
                    'msgVars' => [
1156
                        '%count%' => count($entities),
1157
                    ],
1158
                ];
1159
            }
1160
        } //else don't do anything
1161
1162
        return $this->postActionRedirect(
1163
            array_merge(
1164
                $postActionVars,
1165
                [
1166
                    'flashes' => $flashes,
1167
                ]
1168
            )
1169
        );
1170
    }
1171
1172
    /**
1173
     * Add/remove lead from a list.
1174
     *
1175
     * @param $objectId
1176
     *
1177
     * @return JsonResponse|\Symfony\Component\HttpFoundation\Response
1178
     */
1179
    public function listAction($objectId)
1180
    {
1181
        /** @var \Mautic\LeadBundle\Model\LeadModel $model */
1182
        $model = $this->getModel('lead');
1183
        $lead  = $model->getEntity($objectId);
1184
1185
        if (null != $lead
1186
            && $this->get('mautic.security')->hasEntityAccess(
1187
                'lead:leads:editown',
1188
                'lead:leads:editother',
1189
                $lead->getPermissionUser()
1190
            )
1191
        ) {
1192
            /** @var \Mautic\LeadBundle\Model\ListModel $listModel */
1193
            $listModel = $this->getModel('lead.list');
1194
            $lists     = $listModel->getUserLists();
1195
1196
            // Get a list of lists for the lead
1197
            $leadsLists = $model->getLists($lead, true, true);
1198
        } else {
1199
            $lists = $leadsLists = [];
1200
        }
1201
1202
        return $this->delegateView(
1203
            [
1204
                'viewParameters' => [
1205
                    'lists'      => $lists,
1206
                    'leadsLists' => $leadsLists,
1207
                    'lead'       => $lead,
1208
                ],
1209
                'contentTemplate' => 'MauticLeadBundle:LeadLists:index.html.php',
1210
            ]
1211
        );
1212
    }
1213
1214
    /**
1215
     * Add/remove lead from a company.
1216
     *
1217
     * @param $objectId
1218
     *
1219
     * @return JsonResponse|\Symfony\Component\HttpFoundation\Response
1220
     */
1221
    public function companyAction($objectId)
1222
    {
1223
        /** @var \Mautic\LeadBundle\Model\LeadModel $model */
1224
        $model = $this->getModel('lead');
1225
        $lead  = $model->getEntity($objectId);
1226
1227
        if (null != $lead
1228
            && $this->get('mautic.security')->hasEntityAccess(
1229
                'lead:leads:editown',
1230
                'lead:leads:editother',
1231
                $lead->getOwner()
1232
            )
1233
        ) {
1234
            /** @var \Mautic\LeadBundle\Model\CompanyModel $companyModel */
1235
            $companyModel = $this->getModel('lead.company');
1236
            $companies    = $companyModel->getUserCompanies();
1237
1238
            // Get a list of lists for the lead
1239
            $companyLeads = $lead->getCompanies();
1240
            foreach ($companyLeads as $cl) {
1241
                $companyLead[$cl->getId()] = $cl->getId();
1242
            }
1243
        } else {
1244
            $companies = $companyLead = [];
1245
        }
1246
1247
        return $this->delegateView(
1248
            [
1249
                'viewParameters' => [
1250
                    'companies'   => $companies,
1251
                    'companyLead' => $companyLead,
1252
                    'lead'        => $lead,
1253
                ],
1254
                'contentTemplate' => 'MauticLeadBundle:Lead:company.html.php',
1255
            ]
1256
        );
1257
    }
1258
1259
    /**
1260
     * Add/remove lead from a campaign.
1261
     *
1262
     * @param $objectId
1263
     *
1264
     * @return JsonResponse|\Symfony\Component\HttpFoundation\Response
1265
     */
1266
    public function campaignAction($objectId)
1267
    {
1268
        $model = $this->getModel('lead');
1269
        $lead  = $model->getEntity($objectId);
1270
1271
        if (null != $lead
1272
            && $this->get('mautic.security')->hasEntityAccess(
1273
                'lead:leads:editown',
1274
                'lead:leads:editother',
1275
                $lead->getPermissionUser()
1276
            )
1277
        ) {
1278
            /** @var \Mautic\CampaignBundle\Model\CampaignModel $campaignModel */
1279
            $campaignModel  = $this->getModel('campaign');
1280
            $campaigns      = $campaignModel->getPublishedCampaigns(true);
1281
            $leadsCampaigns = $campaignModel->getLeadCampaigns($lead, true);
1282
1283
            foreach ($campaigns as $c) {
1284
                $campaigns[$c['id']]['inCampaign'] = (isset($leadsCampaigns[$c['id']])) ? true : false;
1285
            }
1286
        } else {
1287
            $campaigns = [];
1288
        }
1289
1290
        return $this->delegateView(
1291
            [
1292
                'viewParameters' => [
1293
                    'campaigns' => $campaigns,
1294
                    'lead'      => $lead,
1295
                ],
1296
                'contentTemplate' => 'MauticLeadBundle:LeadCampaigns:index.html.php',
1297
            ]
1298
        );
1299
    }
1300
1301
    /**
1302
     * @param int $objectId
1303
     *
1304
     * @return JsonResponse
1305
     */
1306
    public function emailAction($objectId = 0)
1307
    {
1308
        $valid = $cancelled = false;
1309
1310
        /** @var \Mautic\LeadBundle\Model\LeadModel $model */
1311
        $model = $this->getModel('lead');
1312
1313
        /** @var \Mautic\LeadBundle\Entity\Lead $lead */
1314
        $lead = $model->getEntity($objectId);
1315
1316
        if (null === $lead
1317
            || !$this->get('mautic.security')->hasEntityAccess(
1318
                'lead:leads:viewown',
1319
                'lead:leads:viewother',
1320
                $lead->getPermissionUser()
1321
            )
1322
        ) {
1323
            return $this->modalAccessDenied();
1324
        }
1325
1326
        $leadFields       = $lead->getProfileFields();
1327
        $leadFields['id'] = $lead->getId();
1328
        $leadEmail        = $leadFields['email'];
1329
        $leadName         = $leadFields['firstname'].' '.$leadFields['lastname'];
1330
        $mailerIsOwner    = $this->get('mautic.helper.core_parameters')->getParameter('mailer_is_owner');
1331
1332
        // Set onwer ID to be the current user ID so it will use his signature
1333
        $leadFields['owner_id'] = $this->get('mautic.helper.user')->getUser()->getId();
1334
1335
        $inList = ('GET' == $this->request->getMethod())
1336
            ? $this->request->get('list', 0)
1337
            : $this->request->request->get(
1338
                'lead_quickemail[list]',
1339
                0,
1340
                true
1341
            );
1342
        $email = ['list' => $inList];
1343
1344
        // Try set owner If should be mailer
1345
        if ($lead->getOwner()) {
1346
            $leadFields['owner_id'] = $lead->getOwner()->getId();
1347
            if ($mailerIsOwner) {
1348
                $email['fromname'] = sprintf(
1349
                    '%s %s',
1350
                    $lead->getOwner()->getFirstName(),
1351
                    $lead->getOwner()->getLastName()
1352
                );
1353
                $email['from'] = $lead->getOwner()->getEmail();
1354
            }
1355
        }
1356
1357
        // Check if lead has a bounce status
1358
        $dnc    = $this->getDoctrine()->getManager()->getRepository('MauticLeadBundle:DoNotContact')->getEntriesByLeadAndChannel($lead, 'email');
1359
        $action = $this->generateUrl('mautic_contact_action', ['objectAction' => 'email', 'objectId' => $objectId]);
1360
        $form   = $this->get('form.factory')->create(EmailType::class, $email, ['action' => $action]);
1361
1362
        if ('POST' == $this->request->getMethod()) {
1363
            $valid = false;
1364
            if (!$cancelled = $this->isFormCancelled($form)) {
1365
                if ($valid = $this->isFormValid($form)) {
1366
                    $email = $form->getData();
1367
1368
                    $bodyCheck = trim(strip_tags($email['body']));
1369
                    if (!empty($bodyCheck)) {
1370
                        $mailer = $this->get('mautic.helper.mailer')->getMailer();
1371
1372
                        // To lead
1373
                        $mailer->addTo($leadEmail, $leadName);
1374
1375
                        // From user
1376
                        $user = $this->get('mautic.helper.user')->getUser();
1377
1378
                        $mailer->setFrom(
1379
                            $email['from'],
1380
                            empty($email['fromname']) ? null : $email['fromname']
1381
                        );
1382
1383
                        // Set Content
1384
                        $mailer->setBody($email['body']);
1385
                        $mailer->parsePlainText($email['body']);
1386
1387
                        // Set lead
1388
                        $mailer->setLead($leadFields);
1389
                        $mailer->setIdHash();
1390
1391
                        $mailer->setSubject($email['subject']);
1392
1393
                        // Ensure safe emoji for notification
1394
                        $subject = EmojiHelper::toHtml($email['subject']);
1395
                        if ($mailer->send(true, false, false)) {
1396
                            $mailer->createEmailStat();
1397
                            $this->addFlash(
1398
                                'mautic.lead.email.notice.sent',
1399
                                [
1400
                                    '%subject%' => $subject,
1401
                                    '%email%'   => $leadEmail,
1402
                                ]
1403
                            );
1404
                        } else {
1405
                            $errors = $mailer->getErrors();
1406
1407
                            // Unset the array of failed email addresses
1408
                            if (isset($errors['failures'])) {
1409
                                unset($errors['failures']);
1410
                            }
1411
1412
                            $form->addError(
1413
                                new FormError(
1414
                                    $this->get('translator')->trans(
1415
                                        'mautic.lead.email.error.failed',
1416
                                        [
1417
                                            '%subject%' => $subject,
1418
                                            '%email%'   => $leadEmail,
1419
                                            '%error%'   => (is_array($errors)) ? implode('<br />', $errors) : $errors,
1420
                                        ],
1421
                                        'flashes'
1422
                                    )
1423
                                )
1424
                            );
1425
                            $valid = false;
1426
                        }
1427
                    } else {
1428
                        $form['body']->addError(
1429
                            new FormError(
1430
                                $this->get('translator')->trans('mautic.lead.email.body.required', [], 'validators')
1431
                            )
1432
                        );
1433
                        $valid = false;
1434
                    }
1435
                }
1436
            }
1437
        }
1438
1439
        if (empty($leadEmail) || $valid || $cancelled) {
1440
            if ($inList) {
1441
                $route          = 'mautic_contact_index';
1442
                $viewParameters = [
1443
                    'page' => $this->get('session')->get('mautic.lead.page', 1),
1444
                ];
1445
                $func = 'index';
1446
            } else {
1447
                $route          = 'mautic_contact_action';
1448
                $viewParameters = [
1449
                    'objectAction' => 'view',
1450
                    'objectId'     => $objectId,
1451
                ];
1452
                $func = 'view';
1453
            }
1454
1455
            return $this->postActionRedirect(
1456
                [
1457
                    'returnUrl'       => $this->generateUrl($route, $viewParameters),
1458
                    'viewParameters'  => $viewParameters,
1459
                    'contentTemplate' => 'MauticLeadBundle:Lead:'.$func,
1460
                    'passthroughVars' => [
1461
                        'mauticContent' => 'lead',
1462
                        'closeModal'    => 1,
1463
                    ],
1464
                ]
1465
            );
1466
        }
1467
1468
        return $this->ajaxAction(
1469
            [
1470
                'contentTemplate' => 'MauticLeadBundle:Lead:email.html.php',
1471
                'viewParameters'  => [
1472
                    'form' => $form->createView(),
1473
                    'dnc'  => end($dnc),
1474
                ],
1475
                'passthroughVars' => [
1476
                    'mauticContent' => 'leadEmail',
1477
                    'route'         => false,
1478
                ],
1479
            ]
1480
        );
1481
    }
1482
1483
    /**
1484
     * Bulk edit lead campaigns.
1485
     *
1486
     * @param int $objectId
1487
     *
1488
     * @return JsonResponse|\Symfony\Component\HttpFoundation\Response
1489
     */
1490
    public function batchCampaignsAction($objectId = 0)
1491
    {
1492
        /** @var \Mautic\CampaignBundle\Model\CampaignModel $campaignModel */
1493
        $campaignModel = $this->getModel('campaign');
1494
1495
        if ('POST' == $this->request->getMethod()) {
1496
            /** @var \Mautic\LeadBundle\Model\LeadModel $model */
1497
            $model = $this->getModel('lead');
1498
            $data  = $this->request->request->get('lead_batch', [], true);
1499
            $ids   = json_decode($data['ids'], true);
1500
1501
            $entities = [];
1502
            if (is_array($ids)) {
1503
                $entities = $model->getEntities(
1504
                    [
1505
                        'filter' => [
1506
                            'force' => [
1507
                                [
1508
                                    'column' => 'l.id',
1509
                                    'expr'   => 'in',
1510
                                    'value'  => $ids,
1511
                                ],
1512
                            ],
1513
                        ],
1514
                        'ignore_paginator' => true,
1515
                    ]
1516
                );
1517
            }
1518
1519
            foreach ($entities as $key => $lead) {
1520
                if (!$this->get('mautic.security')->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser())) {
1521
                    unset($entities[$key]);
1522
                }
1523
            }
1524
1525
            $add    = (!empty($data['add'])) ? $data['add'] : [];
1526
            $remove = (!empty($data['remove'])) ? $data['remove'] : [];
1527
1528
            if ($count = count($entities)) {
1529
                $campaigns = $campaignModel->getEntities(
1530
                    [
1531
                        'filter' => [
1532
                            'force' => [
1533
                                [
1534
                                    'column' => 'c.id',
1535
                                    'expr'   => 'in',
1536
                                    'value'  => array_merge($add, $remove),
1537
                                ],
1538
                            ],
1539
                        ],
1540
                        'ignore_paginator' => true,
1541
                    ]
1542
                );
1543
1544
                /** @var \Mautic\CampaignBundle\Membership\MembershipManager $membershipManager */
1545
                $membershipManager = $this->get('mautic.campaign.membership.manager');
1546
1547
                if (!empty($add)) {
1548
                    foreach ($add as $cid) {
1549
                        $membershipManager->addContacts(new ArrayCollection($entities), $campaigns[$cid]);
1550
                    }
1551
                }
1552
1553
                if (!empty($remove)) {
1554
                    foreach ($remove as $cid) {
1555
                        $membershipManager->removeContacts(new ArrayCollection($entities), $campaigns[$cid]);
1556
                    }
1557
                }
1558
            }
1559
1560
            $this->addFlash(
1561
                'mautic.lead.batch_leads_affected',
1562
                [
1563
                    'pluralCount' => $count,
1564
                    '%count%'     => $count,
1565
                ]
1566
            );
1567
1568
            return new JsonResponse(
1569
                [
1570
                    'closeModal' => true,
1571
                    'flashes'    => $this->getFlashContent(),
1572
                ]
1573
            );
1574
        } else {
1575
            // Get a list of campaigns
1576
            $campaigns = $campaignModel->getPublishedCampaigns(true);
1577
            $items     = [];
1578
            foreach ($campaigns as $campaign) {
1579
                $items[$campaign['name']] = $campaign['id'];
1580
            }
1581
1582
            $route = $this->generateUrl(
1583
                'mautic_contact_action',
1584
                [
1585
                    'objectAction' => 'batchCampaigns',
1586
                ]
1587
            );
1588
1589
            return $this->delegateView(
1590
                [
1591
                    'viewParameters' => [
1592
                        'form' => $this->createForm(
1593
                            BatchType::class,
1594
                            [],
1595
                            [
1596
                                'items'  => $items,
1597
                                'action' => $route,
1598
                            ]
1599
                        )->createView(),
1600
                    ],
1601
                    'contentTemplate' => 'MauticLeadBundle:Batch:form.html.php',
1602
                    'passthroughVars' => [
1603
                        'activeLink'    => '#mautic_contact_index',
1604
                        'mauticContent' => 'leadBatch',
1605
                        'route'         => $route,
1606
                    ],
1607
                ]
1608
            );
1609
        }
1610
    }
1611
1612
    /**
1613
     * Bulk add leads to the DNC list.
1614
     *
1615
     * @param int $objectId
1616
     *
1617
     * @return JsonResponse|\Symfony\Component\HttpFoundation\Response
1618
     */
1619
    public function batchDncAction($objectId = 0)
1620
    {
1621
        if ('POST' == $this->request->getMethod()) {
1622
            /** @var \Mautic\LeadBundle\Model\LeadModel $model */
1623
            $model = $this->getModel('lead');
1624
1625
            /** @var \Mautic\LeadBundle\Model\DoNotContact $doNotContact */
1626
            $doNotContact = $this->get('mautic.lead.model.dnc');
1627
1628
            $data = $this->request->request->get('lead_batch_dnc', [], true);
1629
            $ids  = json_decode($data['ids'], true);
1630
1631
            $entities = [];
1632
            if (is_array($ids)) {
1633
                $entities = $model->getEntities(
1634
                    [
1635
                        'filter' => [
1636
                            'force' => [
1637
                                [
1638
                                    'column' => 'l.id',
1639
                                    'expr'   => 'in',
1640
                                    'value'  => $ids,
1641
                                ],
1642
                            ],
1643
                        ],
1644
                        'ignore_paginator' => true,
1645
                    ]
1646
                );
1647
            }
1648
1649
            if ($count = count($entities)) {
1650
                $persistEntities = [];
1651
                foreach ($entities as $lead) {
1652
                    if ($this->get('mautic.security')->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser())) {
1653
                        if ($doNotContact->addDncForContact($lead->getId(), 'email', DoNotContact::MANUAL, $data['reason'])) {
1654
                            $persistEntities[] = $lead;
1655
                        }
1656
                    }
1657
                }
1658
1659
                // Save entities
1660
                $model->saveEntities($persistEntities);
1661
            }
1662
1663
            $this->addFlash(
1664
                'mautic.lead.batch_leads_affected',
1665
                [
1666
                    'pluralCount' => $count,
1667
                    '%count%'     => $count,
1668
                ]
1669
            );
1670
1671
            return new JsonResponse(
1672
                [
1673
                    'closeModal' => true,
1674
                    'flashes'    => $this->getFlashContent(),
1675
                ]
1676
            );
1677
        } else {
1678
            $route = $this->generateUrl(
1679
                'mautic_contact_action',
1680
                [
1681
                    'objectAction' => 'batchDnc',
1682
                ]
1683
            );
1684
1685
            return $this->delegateView(
1686
                [
1687
                    'viewParameters' => [
1688
                        'form' => $this->createForm(
1689
                            DncType::class,
1690
                            [],
1691
                            [
1692
                                'action' => $route,
1693
                            ]
1694
                        )->createView(),
1695
                    ],
1696
                    'contentTemplate' => 'MauticLeadBundle:Batch:form.html.php',
1697
                    'passthroughVars' => [
1698
                        'activeLink'    => '#mautic_contact_index',
1699
                        'mauticContent' => 'leadBatch',
1700
                        'route'         => $route,
1701
                    ],
1702
                ]
1703
            );
1704
        }
1705
    }
1706
1707
    /**
1708
     * Bulk edit lead stages.
1709
     *
1710
     * @param int $objectId
1711
     *
1712
     * @return JsonResponse|\Symfony\Component\HttpFoundation\Response
1713
     */
1714
    public function batchStagesAction($objectId = 0)
1715
    {
1716
        if ('POST' == $this->request->getMethod()) {
1717
            /** @var \Mautic\LeadBundle\Model\LeadModel $model */
1718
            $model = $this->getModel('lead');
1719
            $data  = $this->request->request->get('lead_batch_stage', [], true);
1720
            $ids   = json_decode($data['ids'], true);
1721
1722
            $entities = [];
1723
            if (is_array($ids)) {
1724
                $entities = $model->getEntities(
1725
                    [
1726
                        'filter' => [
1727
                            'force' => [
1728
                                [
1729
                                    'column' => 'l.id',
1730
                                    'expr'   => 'in',
1731
                                    'value'  => $ids,
1732
                                ],
1733
                            ],
1734
                        ],
1735
                        'ignore_paginator' => true,
1736
                    ]
1737
                );
1738
            }
1739
1740
            $count = 0;
1741
            foreach ($entities as $lead) {
1742
                if ($this->get('mautic.security')->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser())) {
1743
                    ++$count;
1744
1745
                    if (!empty($data['addstage'])) {
1746
                        $stageModel = $this->getModel('stage');
1747
1748
                        $stage = $stageModel->getEntity((int) $data['addstage']);
1749
                        $model->addToStages($lead, $stage);
1750
                    }
1751
1752
                    if (!empty($data['removestage'])) {
1753
                        $stage = $stageModel->getEntity($data['removestage']);
1754
                        $model->removeFromStages($lead, $stage);
1755
                    }
1756
                }
1757
            }
1758
            // Save entities
1759
            $model->saveEntities($entities);
1760
            $this->addFlash(
1761
                'mautic.lead.batch_leads_affected',
1762
                [
1763
                    'pluralCount' => $count,
1764
                    '%count%'     => $count,
1765
                ]
1766
            );
1767
1768
            return new JsonResponse(
1769
                [
1770
                    'closeModal' => true,
1771
                    'flashes'    => $this->getFlashContent(),
1772
                ]
1773
            );
1774
        } else {
1775
            // Get a list of lists
1776
            /** @var \Mautic\StageBundle\Model\StageModel $model */
1777
            $model  = $this->getModel('stage');
1778
            $stages = $model->getUserStages();
1779
            $items  = [];
1780
            foreach ($stages as $stage) {
1781
                $items[$stage['name']] = $stage['id'];
1782
            }
1783
1784
            $route = $this->generateUrl(
1785
                'mautic_contact_action',
1786
                [
1787
                    'objectAction' => 'batchStages',
1788
                ]
1789
            );
1790
1791
            return $this->delegateView(
1792
                [
1793
                    'viewParameters' => [
1794
                        'form' => $this->createForm(
1795
                            StageType::class,
1796
                            [],
1797
                            [
1798
                                'items'  => $items,
1799
                                'action' => $route,
1800
                            ]
1801
                        )->createView(),
1802
                    ],
1803
                    'contentTemplate' => 'MauticLeadBundle:Batch:form.html.php',
1804
                    'passthroughVars' => [
1805
                        'activeLink'    => '#mautic_contact_index',
1806
                        'mauticContent' => 'leadBatch',
1807
                        'route'         => $route,
1808
                    ],
1809
                ]
1810
            );
1811
        }
1812
    }
1813
1814
    /**
1815
     * Bulk edit lead owner.
1816
     *
1817
     * @param int $objectId
1818
     *
1819
     * @return JsonResponse|\Symfony\Component\HttpFoundation\Response
1820
     */
1821
    public function batchOwnersAction($objectId = 0)
1822
    {
1823
        if ('POST' == $this->request->getMethod()) {
1824
            /** @var \Mautic\LeadBundle\Model\LeadModel $model */
1825
            $model = $this->getModel('lead');
1826
            $data  = $this->request->request->get('lead_batch_owner', [], true);
1827
            $ids   = json_decode($data['ids'], true);
1828
1829
            $entities = [];
1830
            if (is_array($ids)) {
1831
                $entities = $model->getEntities(
1832
                    [
1833
                        'filter' => [
1834
                            'force' => [
1835
                                [
1836
                                    'column' => 'l.id',
1837
                                    'expr'   => 'in',
1838
                                    'value'  => $ids,
1839
                                ],
1840
                            ],
1841
                        ],
1842
                        'ignore_paginator' => true,
1843
                    ]
1844
                );
1845
            }
1846
            $count = 0;
1847
            foreach ($entities as $lead) {
1848
                if ($this->get('mautic.security')->hasEntityAccess('lead:leads:editown', 'lead:leads:editother', $lead->getPermissionUser())) {
1849
                    ++$count;
1850
1851
                    if (!empty($data['addowner'])) {
1852
                        $userModel = $this->getModel('user');
1853
                        $user      = $userModel->getEntity((int) $data['addowner']);
1854
                        $lead->setOwner($user);
1855
                    }
1856
                }
1857
            }
1858
            // Save entities
1859
            $model->saveEntities($entities);
1860
            $this->addFlash(
1861
                'mautic.lead.batch_leads_affected',
1862
                [
1863
                    'pluralCount' => $count,
1864
                    '%count%'     => $count,
1865
                ]
1866
            );
1867
1868
            return new JsonResponse(
1869
                [
1870
                    'closeModal' => true,
1871
                    'flashes'    => $this->getFlashContent(),
1872
                ]
1873
            );
1874
        } else {
1875
            $users = $this->getModel('user.user')->getRepository()->getUserList('', 0);
1876
            $items = [];
1877
            foreach ($users as $user) {
1878
                $items[$user['firstName'].' '.$user['lastName']] = $user['id'];
1879
            }
1880
1881
            $route = $this->generateUrl(
1882
                'mautic_contact_action',
1883
                [
1884
                    'objectAction' => 'batchOwners',
1885
                ]
1886
            );
1887
1888
            return $this->delegateView(
1889
                [
1890
                    'viewParameters' => [
1891
                        'form' => $this->createForm(
1892
                            OwnerType::class,
1893
                            [],
1894
                            [
1895
                                'items'  => $items,
1896
                                'action' => $route,
1897
                            ]
1898
                        )->createView(),
1899
                    ],
1900
                    'contentTemplate' => 'MauticLeadBundle:Batch:form.html.php',
1901
                    'passthroughVars' => [
1902
                        'activeLink'    => '#mautic_contact_index',
1903
                        'mauticContent' => 'leadBatch',
1904
                        'route'         => $route,
1905
                    ],
1906
                ]
1907
            );
1908
        }
1909
    }
1910
1911
    /**
1912
     * Bulk export contacts.
1913
     *
1914
     * @return array|JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\StreamedResponse
1915
     */
1916
    public function batchExportAction()
1917
    {
1918
        //set some permissions
1919
        $permissions = $this->get('mautic.security')->isGranted(
1920
            [
1921
                'lead:leads:viewown',
1922
                'lead:leads:viewother',
1923
                'lead:leads:create',
1924
                'lead:leads:editown',
1925
                'lead:leads:editother',
1926
                'lead:leads:deleteown',
1927
                'lead:leads:deleteother',
1928
            ],
1929
            'RETURN_ARRAY'
1930
        );
1931
1932
        if (!$permissions['lead:leads:viewown'] && !$permissions['lead:leads:viewother']) {
1933
            return $this->accessDenied();
1934
        }
1935
1936
        /** @var \Mautic\LeadBundle\Model\LeadModel $model */
1937
        $model      = $this->getModel('lead');
1938
        $session    = $this->get('session');
1939
        $search     = $session->get('mautic.lead.filter', '');
1940
        $orderBy    = $session->get('mautic.lead.orderby', 'l.last_active');
1941
        $orderByDir = $session->get('mautic.lead.orderbydir', 'DESC');
1942
        $ids        = $this->request->get('ids');
1943
1944
        $filter     = ['string' => $search, 'force' => ''];
1945
        $translator = $this->get('translator');
1946
        $anonymous  = $translator->trans('mautic.lead.lead.searchcommand.isanonymous');
1947
        $mine       = $translator->trans('mautic.core.searchcommand.ismine');
1948
        $indexMode  = $session->get('mautic.lead.indexmode', 'list');
1949
        $dataType   = $this->request->get('filetype');
1950
1951
        if (!empty($ids)) {
1952
            $filter['force'] = [
1953
                [
1954
                    'column' => 'l.id',
1955
                    'expr'   => 'in',
1956
                    'value'  => json_decode($ids, true),
1957
                ],
1958
            ];
1959
        } else {
1960
            if ('list' != $indexMode || ('list' == $indexMode && false === strpos($search, $anonymous))) {
1961
                //remove anonymous leads unless requested to prevent clutter
1962
                $filter['force'] .= " !$anonymous";
1963
            }
1964
1965
            if (!$permissions['lead:leads:viewother']) {
1966
                $filter['force'] .= " $mine";
1967
            }
1968
        }
1969
1970
        $args = [
1971
            'start'          => 0,
1972
            'limit'          => 200,
1973
            'filter'         => $filter,
1974
            'orderBy'        => $orderBy,
1975
            'orderByDir'     => $orderByDir,
1976
            'withTotalCount' => true,
1977
        ];
1978
1979
        $resultsCallback = function ($contact) {
1980
            return $contact->getProfileFields();
1981
        };
1982
1983
        $iterator = new IteratorExportDataModel($model, $args, $resultsCallback);
1984
1985
        return $this->exportResultsAs($iterator, $dataType, 'contacts');
1986
    }
1987
1988
    /**
1989
     * @param $contactId
1990
     *
1991
     * @return array|JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\StreamedResponse
1992
     */
1993
    public function contactExportAction($contactId)
1994
    {
1995
        //set some permissions
1996
        $permissions = $this->get('mautic.security')->isGranted(
1997
            [
1998
                'lead:leads:viewown',
1999
                'lead:leads:viewother',
2000
            ],
2001
            'RETURN_ARRAY'
2002
        );
2003
2004
        if (!$permissions['lead:leads:viewown'] && !$permissions['lead:leads:viewother']) {
2005
            return $this->accessDenied();
2006
        }
2007
2008
        /** @var LeadModel $leadModel */
2009
        $leadModel = $this->getModel('lead.lead');
2010
        $lead      = $leadModel->getEntity($contactId);
2011
        $dataType  = $this->request->get('filetype', 'csv');
2012
2013
        if (empty($lead)) {
2014
            return $this->notFound();
2015
        }
2016
2017
        $contactFields = $lead->getProfileFields();
2018
        $export        = [];
2019
        foreach ($contactFields as $alias => $contactField) {
2020
            $export[] = [
2021
                'alias' => $alias,
2022
                'value' => $contactField,
2023
            ];
2024
        }
2025
2026
        return $this->exportResultsAs($export, $dataType, 'contact_data_'.($contactFields['email'] ?: $contactFields['id']));
2027
    }
2028
}
2029