Issues (3627)

bundles/LeadBundle/Controller/FieldController.php (3 issues)

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\DBAL\DBALException;
15
use Mautic\CoreBundle\Controller\FormController;
16
use Mautic\LeadBundle\Entity\LeadField;
17
use Mautic\LeadBundle\Model\FieldModel;
18
use Symfony\Component\Form\FormError;
19
20
class FieldController extends FormController
21
{
22
    /**
23
     * Generate's default list view.
24
     *
25
     * @param int $page
26
     *
27
     * @return array|\Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
28
     */
29
    public function indexAction($page = 1)
30
    {
31
        //set some permissions
32
        $permissions = $this->get('mautic.security')->isGranted(['lead:fields:view', 'lead:fields:full'], 'RETURN_ARRAY');
33
34
        $session = $this->get('session');
35
36
        if (!$permissions['lead:fields:view'] && !$permissions['lead:fields:full']) {
37
            return $this->accessDenied();
38
        }
39
40
        $this->setListFilters();
41
42
        $limit  = $session->get('mautic.leadfield.limit', $this->coreParametersHelper->get('default_pagelimit'));
43
        $search = $this->request->get('search', $session->get('mautic.leadfield.filter', ''));
44
        $session->set('mautic.leadfilter.filter', $search);
45
46
        //do some default filtering
47
        $orderBy    = $this->get('session')->get('mautic.leadfilter.orderby', 'f.order');
48
        $orderByDir = $this->get('session')->get('mautic.leadfilter.orderbydir', 'ASC');
49
50
        $start = (1 === $page) ? 0 : (($page - 1) * $limit);
51
        if ($start < 0) {
52
            $start = 0;
53
        }
54
55
        $request = $this->factory->getRequest();
56
        $search  = $request->get('search', $session->get('mautic.lead.emailtoken.filter', ''));
57
58
        $session->set('mautic.lead.emailtoken.filter', $search);
59
60
        $fields = $this->getModel('lead.field')->getEntities([
61
            'start'      => $start,
62
            'limit'      => $limit,
63
            'filter'     => ['string' => $search],
64
            'orderBy'    => $orderBy,
65
            'orderByDir' => $orderByDir,
66
        ]);
67
        $count = count($fields);
68
69
        if ($count && $count < ($start + 1)) {
70
            //the number of entities are now less then the current page so redirect to the last page
71
            if (1 === $count) {
72
                $lastPage = 1;
73
            } else {
74
                $lastPage = (ceil($count / $limit)) ?: 1;
75
            }
76
            $session->set('mautic.leadfield.page', $lastPage);
77
            $returnUrl = $this->generateUrl('mautic_contactfield_index', ['page' => $lastPage]);
78
79
            return $this->postActionRedirect([
80
                'returnUrl'       => $returnUrl,
81
                'viewParameters'  => ['page' => $lastPage],
82
                'contentTemplate' => 'MauticLeadBundle:Field:index',
83
                'passthroughVars' => [
84
                    'activeLink'    => '#mautic_contactfield_index',
85
                    'mauticContent' => 'leadfield',
86
                ],
87
            ]);
88
        }
89
90
        //set what page currently on so that we can return here after form submission/cancellation
91
        $session->set('mautic.leadfield.page', $page);
92
93
        $tmpl = $this->request->isXmlHttpRequest() ? $this->request->get('tmpl', 'index') : 'index';
94
95
        return $this->delegateView([
96
            'viewParameters' => [
97
                'items'       => $fields,
98
                'searchValue' => $search,
99
                'permissions' => $permissions,
100
                'tmpl'        => $tmpl,
101
                'totalItems'  => $count,
102
                'limit'       => $limit,
103
                'page'        => $page,
104
            ],
105
            'contentTemplate' => 'MauticLeadBundle:Field:list.html.php',
106
            'passthroughVars' => [
107
                'activeLink'    => '#mautic_contactfield_index',
108
                'route'         => $this->generateUrl('mautic_contactfield_index', ['page' => $page]),
109
                'mauticContent' => 'leadfield',
110
            ],
111
        ]);
112
    }
113
114
    /**
115
     * Generate's new form and processes post data.
116
     *
117
     * @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
118
     */
119
    public function newAction()
120
    {
121
        if (!$this->get('mautic.security')->isGranted('lead:fields:full')) {
122
            return $this->accessDenied();
123
        }
124
125
        //retrieve the entity
126
        $field = new LeadField();
127
        /** @var FieldModel $model */
128
        $model = $this->getModel('lead.field');
129
        //set the return URL for post actions
130
        $returnUrl = $this->generateUrl('mautic_contactfield_index');
131
        $action    = $this->generateUrl('mautic_contactfield_action', ['objectAction' => 'new']);
132
        //get the user form factory
133
        $form = $model->createForm($field, $this->get('form.factory'), $action);
134
135
        ///Check for a submitted form and process it
136
        if ('POST' == $this->request->getMethod()) {
137
            $valid = false;
138
            if (!$cancelled = $this->isFormCancelled($form)) {
139
                if ($valid = $this->isFormValid($form)) {
140
                    $request = $this->request->request->all();
141
                    if (isset($request['leadfield']['properties'])) {
142
                        $result = $model->setFieldProperties($field, $request['leadfield']['properties']);
143
                        if (true !== $result) {
144
                            //set the error
145
                            $form->get('properties')->addError(
146
                                new FormError(
147
                                    $this->get('translator')->trans($result, [], 'validators')
148
                                )
149
                            );
150
                            $valid = false;
151
                        }
152
                    }
153
154
                    if ($valid) {
155
                        $flashMessage = 'mautic.core.notice.created';
156
                        try {
157
                            //form is valid so process the data
158
                            $model->saveEntity($field);
159
                        } catch (DBALException $ee) {
160
                            $flashMessage = $ee->getMessage();
161
                        } catch (\Exception $e) {
162
                            $form['alias']->addError(
163
                                    new FormError(
164
                                        $this->get('translator')->trans('mautic.lead.field.failed', ['%error%' => $e->getMessage()], 'validators')
165
                                    )
166
                                );
167
                            $valid = false;
168
                        }
169
                        $this->addFlash(
170
                                $flashMessage,
171
                                [
172
                                    '%name%'      => $field->getLabel(),
173
                                    '%menu_link%' => 'mautic_contactfield_index',
174
                                    '%url%'       => $this->generateUrl(
175
                                        'mautic_contactfield_action',
176
                                        [
177
                                            'objectAction' => 'edit',
178
                                            'objectId'     => $field->getId(),
179
                                        ]
180
                                    ),
181
                                ]
182
                            );
183
                    }
184
                }
185
            }
186
187
            if ($cancelled || ($valid && $form->get('buttons')->get('save')->isClicked())) {
188
                return $this->postActionRedirect(
189
                    [
190
                        'returnUrl'       => $returnUrl,
191
                        'contentTemplate' => 'MauticLeadBundle:Field:index',
192
                        'passthroughVars' => [
193
                            'activeLink'    => '#mautic_contactfield_index',
194
                            'mauticContent' => 'leadfield',
195
                        ],
196
                    ]
197
                );
198
            } elseif ($valid && !$cancelled) {
199
                return $this->editAction($field->getId(), true);
200
            } elseif (!$valid) {
201
                // some bug in Symfony prevents repopulating list options on errors
202
                $field   = $form->getData();
203
                $newForm = $model->createForm($field, $this->get('form.factory'), $action);
204
                $this->copyErrorsRecursively($form, $newForm);
205
                $form = $newForm;
206
            }
207
        }
208
209
        return $this->delegateView(
210
            [
211
                'viewParameters' => [
212
                    'form' => $form->createView(),
213
                ],
214
                'contentTemplate' => 'MauticLeadBundle:Field:form.html.php',
215
                'passthroughVars' => [
216
                    'activeLink'    => '#mautic_contactfield_index',
217
                    'route'         => $this->generateUrl('mautic_contactfield_action', ['objectAction' => 'new']),
218
                    'mauticContent' => 'leadfield',
219
                ],
220
            ]
221
        );
222
    }
223
224
    /**
225
     * Generate's edit form and processes post data.
226
     *
227
     * @param            $objectId
228
     * @param bool|false $ignorePost
229
     *
230
     * @return array|\Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
231
     */
232
    public function editAction($objectId, $ignorePost = false)
233
    {
234
        if (!$this->get('mautic.security')->isGranted('lead:fields:full')) {
235
            return $this->accessDenied();
236
        }
237
238
        /** @var FieldModel $model */
239
        $model = $this->getModel('lead.field');
240
        $field = $model->getEntity($objectId);
241
242
        //set the return URL
243
        $returnUrl = $this->generateUrl('mautic_contactfield_index');
244
245
        $postActionVars = [
246
            'returnUrl'       => $returnUrl,
247
            'contentTemplate' => 'MauticLeadBundle:Field:index',
248
            'passthroughVars' => [
249
                'activeLink'    => '#mautic_contactfield_index',
250
                'mauticContent' => 'leadfield',
251
            ],
252
        ];
253
        //list not found
254
        if (null === $field) {
255
            return $this->postActionRedirect(
256
                array_merge($postActionVars, [
257
                    'flashes' => [
258
                        [
259
                            'type'    => 'error',
260
                            'msg'     => 'mautic.lead.field.error.notfound',
261
                            'msgVars' => ['%id%' => $objectId],
262
                        ],
263
                    ],
264
                ])
265
            );
266
        } elseif ($model->isLocked($field)) {
267
            //deny access if the entity is locked
268
            return $this->isLocked($postActionVars, $field, 'lead.field');
269
        }
270
271
        $action = $this->generateUrl('mautic_contactfield_action', ['objectAction' => 'edit', 'objectId' => $objectId]);
272
        $form   = $model->createForm($field, $this->get('form.factory'), $action);
273
274
        ///Check for a submitted form and process it
275
        if (!$ignorePost && 'POST' == $this->request->getMethod()) {
276
            $valid = false;
277
            if (!$cancelled = $this->isFormCancelled($form)) {
278
                if ($valid = $this->isFormValid($form)) {
279
                    $request = $this->request->request->all();
280
                    if (isset($request['leadfield']['properties'])) {
281
                        $result = $model->setFieldProperties($field, $request['leadfield']['properties']);
282
                        if (true !== $result) {
283
                            //set the error
284
                            $form->get('properties')->addError(new FormError(
285
                                $this->get('translator')->trans($result, [], 'validators')
286
                            ));
287
                            $valid = false;
288
                        }
289
                    }
290
291
                    if ($valid) {
292
                        //form is valid so process the data
293
                        $model->saveEntity($field, $form->get('buttons')->get('save')->isClicked());
294
295
                        $this->addFlash('mautic.core.notice.updated', [
296
                            '%name%'      => $field->getLabel(),
297
                            '%menu_link%' => 'mautic_contactfield_index',
298
                            '%url%'       => $this->generateUrl('mautic_contactfield_action', [
299
                                'objectAction' => 'edit',
300
                                'objectId'     => $field->getId(),
301
                            ]),
302
                        ]);
303
                    }
304
                }
305
            } else {
306
                //unlock the entity
307
                $model->unlockEntity($field);
308
            }
309
310
            if ($cancelled || ($valid && $form->get('buttons')->get('save')->isClicked())) {
311
                return $this->postActionRedirect(
312
                    array_merge($postActionVars, [
313
                            'viewParameters'  => ['objectId' => $field->getId()],
314
                            'contentTemplate' => 'MauticLeadBundle:Field:index',
315
                        ]
316
                    )
317
                );
318
            } elseif ($valid) {
319
                // Rebuild the form with new action so that apply doesn't keep creating a clone
320
                $action = $this->generateUrl('mautic_contactfield_action', ['objectAction' => 'edit', 'objectId' => $field->getId()]);
321
                $form   = $model->createForm($field, $this->get('form.factory'), $action);
322
            } else {
323
                // some bug in Symfony prevents repopulating list options on errors
324
                $field   = $form->getData();
325
                $newForm = $model->createForm($field, $this->get('form.factory'), $action);
326
                $this->copyErrorsRecursively($form, $newForm);
327
                $form = $newForm;
328
            }
329
        } else {
330
            //lock the entity
331
            $model->lockEntity($field);
332
        }
333
334
        return $this->delegateView([
335
            'viewParameters' => [
336
                'form' => $form->createView(),
337
            ],
338
            'contentTemplate' => 'MauticLeadBundle:Field:form.html.php',
339
            'passthroughVars' => [
340
                'activeLink'    => '#mautic_contactfield_index',
341
                'route'         => $action,
342
                'mauticContent' => 'leadfield',
343
            ],
344
        ]);
345
    }
346
347
    /**
348
     * Clone an entity.
349
     *
350
     * @param $objectId
351
     *
352
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
353
     */
354
    public function cloneAction($objectId)
355
    {
356
        $model  = $this->getModel('lead.field');
357
        $entity = $model->getEntity($objectId);
358
359
        if (null != $entity) {
360
            if (!$this->get('mautic.security')->isGranted('lead:fields:full')) {
361
                return $this->accessDenied();
362
            }
363
364
            $clone = clone $entity;
365
            $clone->setIsPublished(false);
366
            $clone->setIsFixed(false);
367
            $this->get('mautic.helper.field.alias')->makeAliasUnique($clone);
368
            $model->saveEntity($clone);
369
            $objectId = $clone->getId();
370
        }
371
372
        return $this->editAction($objectId);
373
    }
374
375
    /**
376
     * Delete a field.
377
     *
378
     * @param $objectId
379
     *
380
     * @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse
381
     */
382
    public function deleteAction($objectId)
383
    {
384
        if (!$this->get('mautic.security')->isGranted('lead:fields:full')) {
385
            return $this->accessDenied();
386
        }
387
388
        $returnUrl = $this->generateUrl('mautic_contactfield_index');
389
        $flashes   = [];
390
391
        $postActionVars = [
392
            'returnUrl'       => $returnUrl,
393
            'contentTemplate' => 'MauticLeadBundle:Field:index',
394
            'passthroughVars' => [
395
                'activeLink'    => '#mautic_contactfield_index',
396
                'mauticContent' => 'lead',
397
            ],
398
        ];
399
400
        if ('POST' == $this->request->getMethod()) {
401
            /** @var FieldModel $model */
402
            $model = $this->getModel('lead.field');
403
            $field = $model->getEntity($objectId);
404
405
            if (null === $field) {
406
                $flashes[] = [
407
                    'type'    => 'error',
408
                    'msg'     => 'mautic.lead.field.error.notfound',
409
                    'msgVars' => ['%id%' => $objectId],
410
                ];
411
            } elseif ($model->isLocked($field)) {
412
                return $this->isLocked($postActionVars, $field, 'lead.field');
413
            } elseif ($field->isFixed()) {
414
                //cannot delete fixed fields
415
                return $this->accessDenied();
416
            }
417
418
            $segments = [];
419
            foreach ($model->getFieldSegments($field) as $segment) {
0 ignored issues
show
It seems like $field can also be of type null; however, parameter $field of Mautic\LeadBundle\Model\...del::getFieldSegments() does only seem to accept Mautic\LeadBundle\Entity\LeadField, maybe add an additional type check? ( Ignorable by Annotation )

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

419
            foreach ($model->getFieldSegments(/** @scrutinizer ignore-type */ $field) as $segment) {
Loading history...
420
                $segments[] = sprintf('"%s" (%d)', $segment->getName(), $segment->getId());
421
            }
422
423
            if (count($segments)) {
424
                $flashMessage = [
425
                    'type'    => 'error',
426
                    'msg'     => 'mautic.core.notice.used.field',
427
                    'msgVars' => [
428
                        '%name%'     => $field->getLabel(),
429
                        '%id%'       => $objectId,
430
                        '%segments%' => implode(', ', $segments),
431
                    ],
432
                ];
433
            } else {
434
                $model->deleteEntity($field);
435
                $flashMessage = [
436
                    'type'    => 'notice',
437
                    'msg'     => 'mautic.core.notice.deleted',
438
                    'msgVars' => [
439
                        '%name%' => $field->getLabel(),
440
                        '%id%'   => $objectId,
441
                    ],
442
                ];
443
            }
444
445
            $flashes[] = $flashMessage;
446
        } //else don't do anything
447
448
        return $this->postActionRedirect(
449
            array_merge($postActionVars, [
450
                'flashes' => $flashes,
451
            ])
452
        );
453
    }
454
455
    /**
456
     * Deletes a group of entities.
457
     *
458
     * @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse
459
     */
460
    public function batchDeleteAction()
461
    {
462
        if (!$this->get('mautic.security')->isGranted('lead:fields:full')) {
463
            return $this->accessDenied();
464
        }
465
466
        $returnUrl = $this->generateUrl('mautic_contactfield_index');
467
        $flashes   = [];
468
469
        $postActionVars = [
470
            'returnUrl'       => $returnUrl,
471
            'contentTemplate' => 'MauticLeadBundle:Field:index',
472
            'passthroughVars' => [
473
                'activeLink'    => '#mautic_contactfield_index',
474
                'mauticContent' => 'lead',
475
            ],
476
        ];
477
478
        if ('POST' == $this->request->getMethod()) {
479
            /** @var FieldModel $model */
480
            $model     = $this->getModel('lead.field');
481
            $ids       = json_decode($this->request->query->get('ids', '{}'));
482
            $deleteIds = [];
483
484
            // Loop over the IDs to perform access checks pre-delete
485
            foreach ($ids as $objectId) {
486
                $entity = $model->getEntity($objectId);
487
488
                if (null === $entity) {
489
                    $flashes[] = [
490
                        'type'    => 'error',
491
                        'msg'     => 'mautic.lead.field.error.notfound',
492
                        'msgVars' => ['%id%' => $objectId],
493
                    ];
494
                } elseif ($entity->isFixed()) {
495
                    $flashes[] = $this->accessDenied(true);
496
                } elseif ($model->isLocked($entity)) {
497
                    $flashes[] = $this->isLocked($postActionVars, $entity, 'lead.field', true);
498
                } else {
499
                    $deleteIds[] = $objectId;
500
                }
501
            }
502
503
            // Delete everything we are able to
504
            if (!empty($deleteIds)) {
505
                $filteredDeleteIds = $model->filterUsedFieldIds($deleteIds);
506
                $usedFieldIds      = array_diff($deleteIds, $filteredDeleteIds);
507
                $segments          = [];
508
                $usedFieldsNames   = [];
509
510
                if ($usedFieldIds) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $usedFieldIds of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
511
                    // Iterating through all used fileds to get segments they are used in
512
                    foreach ($usedFieldIds as $usedFieldId) {
513
                        $fieldEntity = $model->getEntity($usedFieldId);
514
                        foreach ($model->getFieldSegments($fieldEntity) as $segment) {
0 ignored issues
show
It seems like $fieldEntity can also be of type null; however, parameter $field of Mautic\LeadBundle\Model\...del::getFieldSegments() does only seem to accept Mautic\LeadBundle\Entity\LeadField, maybe add an additional type check? ( Ignorable by Annotation )

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

514
                        foreach ($model->getFieldSegments(/** @scrutinizer ignore-type */ $fieldEntity) as $segment) {
Loading history...
515
                            $segments[$segment->getId()] = sprintf('"%s" (%d)', $segment->getName(), $segment->getId());
516
                            $usedFieldsNames[]           = sprintf('"%s"', $fieldEntity->getName());
517
                        }
518
                    }
519
                }
520
521
                if ($filteredDeleteIds !== $deleteIds) {
522
                    $flashes[] = [
523
                        'type'    => 'error',
524
                        'msg'     => 'mautic.core.notice.used.fields',
525
                        'msgVars' => [
526
                            '%segments%' => implode(', ', $segments),
527
                            '%fields%'   => implode(', ', array_unique($usedFieldsNames)),
528
                        ],
529
                    ];
530
                }
531
532
                if (count($filteredDeleteIds)) {
533
                    $entities = $model->deleteEntities($filteredDeleteIds);
534
535
                    $flashes[] = [
536
                        'type'    => 'notice',
537
                        'msg'     => 'mautic.lead.field.notice.batch_deleted',
538
                        'msgVars' => [
539
                            '%count%' => count($entities),
540
                        ],
541
                    ];
542
                }
543
            }
544
        } //else don't do anything
545
546
        return $this->postActionRedirect(
547
            array_merge($postActionVars, [
548
                'flashes' => $flashes,
549
            ])
550
        );
551
    }
552
}
553