Completed
Push — 3.x ( de469d...59ade0 )
by Grégoire
04:08
created

CRUDController::deleteAction()   B

Complexity

Conditions 7
Paths 13

Size

Total Lines 66
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 66
rs 7.0832
c 0
b 0
f 0
cc 7
eloc 39
nc 13
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the Sonata Project package.
5
 *
6
 * (c) Thomas Rabaix <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sonata\AdminBundle\Controller;
13
14
use Doctrine\Common\Inflector\Inflector;
15
use Psr\Log\LoggerInterface;
16
use Psr\Log\NullLogger;
17
use Sonata\AdminBundle\Admin\AdminInterface;
18
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
19
use Sonata\AdminBundle\Exception\LockException;
20
use Sonata\AdminBundle\Exception\ModelManagerException;
21
use Sonata\AdminBundle\Util\AdminObjectAclData;
22
use Sonata\AdminBundle\Util\AdminObjectAclManipulator;
23
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
24
use Symfony\Component\DependencyInjection\ContainerInterface;
25
use Symfony\Component\Form\FormView;
26
use Symfony\Component\HttpFoundation\JsonResponse;
27
use Symfony\Component\HttpFoundation\RedirectResponse;
28
use Symfony\Component\HttpFoundation\Request;
29
use Symfony\Component\HttpFoundation\Response;
30
use Symfony\Component\HttpKernel\Exception\HttpException;
31
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
32
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
33
use Symfony\Component\Security\Csrf\CsrfToken;
34
35
/**
36
 * @author Thomas Rabaix <[email protected]>
37
 */
38
class CRUDController extends Controller
39
{
40
    /**
41
     * The related Admin class.
42
     *
43
     * @var AdminInterface
44
     */
45
    protected $admin;
46
47
    /**
48
     * Sets the Container associated with this Controller.
49
     *
50
     * @param ContainerInterface $container A ContainerInterface instance
51
     */
52
    public function setContainer(ContainerInterface $container = null)
53
    {
54
        $this->container = $container;
55
56
        $this->configure();
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function render($view, array $parameters = [], Response $response = null)
63
    {
64
        if (!$this->isXmlHttpRequest()) {
65
            $parameters['breadcrumbs_builder'] = $this->get('sonata.admin.breadcrumbs_builder');
66
        }
67
        $parameters['admin'] = isset($parameters['admin']) ?
68
            $parameters['admin'] :
69
            $this->admin;
70
71
        $parameters['base_template'] = isset($parameters['base_template']) ?
72
            $parameters['base_template'] :
73
            $this->getBaseTemplate();
74
75
        $parameters['admin_pool'] = $this->get('sonata.admin.pool');
76
77
        return parent::render($view, $parameters, $response);
78
    }
79
80
    /**
81
     * List action.
82
     *
83
     * @return Response
84
     *
85
     * @throws AccessDeniedException If access is not granted
86
     */
87
    public function listAction()
88
    {
89
        $request = $this->getRequest();
90
91
        $this->admin->checkAccess('list');
92
93
        $preResponse = $this->preList($request);
94
        if ($preResponse !== null) {
95
            return $preResponse;
96
        }
97
98
        if ($listMode = $request->get('_list_mode')) {
99
            $this->admin->setListMode($listMode);
100
        }
101
102
        $datagrid = $this->admin->getDatagrid();
103
        $formView = $datagrid->getForm()->createView();
104
105
        // set the theme for the current Admin Form
106
        $this->setFormTheme($formView, $this->admin->getFilterTheme());
0 ignored issues
show
Documentation introduced by
$this->admin->getFilterTheme() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
107
108
        return $this->render($this->admin->getTemplate('list'), [
109
            'action' => 'list',
110
            'form' => $formView,
111
            'datagrid' => $datagrid,
112
            'csrf_token' => $this->getCsrfToken('sonata.batch'),
113
            'export_formats' => $this->has('sonata.admin.admin_exporter') ?
114
                $this->get('sonata.admin.admin_exporter')->getAvailableFormats($this->admin) :
115
                $this->admin->getExportFormats(),
116
        ], null);
117
    }
118
119
    /**
120
     * Execute a batch delete.
121
     *
122
     * @param ProxyQueryInterface $query
123
     *
124
     * @return RedirectResponse
125
     *
126
     * @throws AccessDeniedException If access is not granted
127
     */
128
    public function batchActionDelete(ProxyQueryInterface $query)
129
    {
130
        $this->admin->checkAccess('batchDelete');
131
132
        $modelManager = $this->admin->getModelManager();
133
134
        try {
135
            $modelManager->batchDelete($this->admin->getClass(), $query);
136
            $this->addFlash(
137
                'sonata_flash_success',
138
                $this->trans('flash_batch_delete_success', [], 'SonataAdminBundle')
139
            );
140
        } catch (ModelManagerException $e) {
141
            $this->handleModelManagerException($e);
142
            $this->addFlash(
143
                'sonata_flash_error',
144
                $this->trans('flash_batch_delete_error', [], 'SonataAdminBundle')
145
            );
146
        }
147
148
        return new RedirectResponse($this->admin->generateUrl(
149
            'list',
150
            ['filter' => $this->admin->getFilterParameters()]
151
        ));
152
    }
153
154
    /**
155
     * Delete action.
156
     *
157
     * @param int|string|null $id
158
     *
159
     * @return Response|RedirectResponse
160
     *
161
     * @throws NotFoundHttpException If the object does not exist
162
     * @throws AccessDeniedException If access is not granted
163
     */
164
    public function deleteAction($id)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
165
    {
166
        $request = $this->getRequest();
167
168
        $id = $this->getObjectIdFromRouteIdHandler();
169
170
        $object = $this->admin->getObject($id);
171
172
        if (!$object) {
173
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
174
        }
175
176
        $this->admin->checkAccess('delete', $object);
177
178
        $preResponse = $this->preDelete($request, $object);
179
        if ($preResponse !== null) {
180
            return $preResponse;
181
        }
182
183
        if ($this->getRestMethod() == 'DELETE') {
184
            // check the csrf token
185
            $this->validateCsrfToken('sonata.delete');
186
187
            $objectName = $this->admin->toString($object);
188
189
            try {
190
                $this->admin->delete($object);
191
192
                if ($this->isXmlHttpRequest()) {
193
                    return $this->renderJson(['result' => 'ok'], 200, []);
194
                }
195
196
                $this->addFlash(
197
                    'sonata_flash_success',
198
                    $this->trans(
199
                        'flash_delete_success',
200
                        ['%name%' => $this->escapeHtml($objectName)],
201
                        'SonataAdminBundle'
202
                    )
203
                );
204
            } catch (ModelManagerException $e) {
205
                $this->handleModelManagerException($e);
206
207
                if ($this->isXmlHttpRequest()) {
208
                    return $this->renderJson(['result' => 'error'], 200, []);
209
                }
210
211
                $this->addFlash(
212
                    'sonata_flash_error',
213
                    $this->trans(
214
                        'flash_delete_error',
215
                        ['%name%' => $this->escapeHtml($objectName)],
216
                        'SonataAdminBundle'
217
                    )
218
                );
219
            }
220
221
            return $this->redirectTo($object);
222
        }
223
224
        return $this->render($this->admin->getTemplate('delete'), [
225
            'object' => $object,
226
            'action' => 'delete',
227
            'csrf_token' => $this->getCsrfToken('sonata.delete'),
228
        ], null);
229
    }
230
231
    /**
232
     * Edit action.
233
     *
234
     * @param int|string|null $id
235
     *
236
     * @return Response|RedirectResponse
237
     *
238
     * @throws NotFoundHttpException If the object does not exist
239
     * @throws AccessDeniedException If access is not granted
240
     */
241
    public function editAction($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
242
    {
243
        // the key used to lookup the template
244
        $templateKey = 'edit';
245
246
        $request = $this->getRequest();
247
        $id = $this->getObjectIdFromRouteIdHandler();
248
        $existingObject = $this->admin->getObject($id);
249
250
        if (!$existingObject) {
251
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
252
        }
253
254
        $this->admin->checkAccess('edit', $existingObject);
255
256
        $preResponse = $this->preEdit($request, $existingObject);
257
        if ($preResponse !== null) {
258
            return $preResponse;
259
        }
260
261
        $this->admin->setSubject($existingObject);
262
        $objectId = $this->admin->getNormalizedIdentifier($existingObject);
263
264
        /** @var $form Form */
265
        $form = $this->admin->getForm();
266
        $form->setData($existingObject);
267
        $form->handleRequest($request);
268
269
        if ($form->isSubmitted()) {
270
            //TODO: remove this check for 4.0
271
            if (method_exists($this->admin, 'preValidate')) {
272
                $this->admin->preValidate($existingObject);
0 ignored issues
show
Bug introduced by
The method preValidate() does not exist on Sonata\AdminBundle\Admin\AdminInterface. Did you maybe mean validate()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
273
            }
274
            $isFormValid = $form->isValid();
275
276
            // persist if the form was valid and if in preview mode the preview was approved
277
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
278
                $submittedObject = $form->getData();
279
                $this->admin->setSubject($submittedObject);
280
281
                try {
282
                    $existingObject = $this->admin->update($submittedObject);
283
284
                    if ($this->isXmlHttpRequest()) {
285
                        return $this->renderJson([
286
                            'result' => 'ok',
287
                            'objectId' => $objectId,
288
                            'objectName' => $this->escapeHtml($this->admin->toString($existingObject)),
289
                        ], 200, []);
290
                    }
291
292
                    $this->addFlash(
293
                        'sonata_flash_success',
294
                        $this->trans(
295
                            'flash_edit_success',
296
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
297
                            'SonataAdminBundle'
298
                        )
299
                    );
300
301
                    // redirect to edit mode
302
                    return $this->redirectTo($existingObject);
303
                } catch (ModelManagerException $e) {
304
                    $this->handleModelManagerException($e);
305
306
                    $isFormValid = false;
307
                } catch (LockException $e) {
308
                    $this->addFlash('sonata_flash_error', $this->trans('flash_lock_error', [
309
                        '%name%' => $this->escapeHtml($this->admin->toString($existingObject)),
310
                        '%link_start%' => '<a href="'.$this->admin->generateObjectUrl('edit', $existingObject).'">',
311
                        '%link_end%' => '</a>',
312
                    ], 'SonataAdminBundle'));
313
                }
314
            }
315
316
            // show an error message if the form failed validation
317
            if (!$isFormValid) {
318
                if (!$this->isXmlHttpRequest()) {
319
                    $this->addFlash(
320
                        'sonata_flash_error',
321
                        $this->trans(
322
                            'flash_edit_error',
323
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
324
                            'SonataAdminBundle'
325
                        )
326
                    );
327
                }
328
            } elseif ($this->isPreviewRequested()) {
329
                // enable the preview template if the form was valid and preview was requested
330
                $templateKey = 'preview';
331
                $this->admin->getShow();
332
            }
333
        }
334
335
        $formView = $form->createView();
336
        // set the theme for the current Admin Form
337
        $this->setFormTheme($formView, $this->admin->getFormTheme());
0 ignored issues
show
Documentation introduced by
$this->admin->getFormTheme() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
338
339
        return $this->render($this->admin->getTemplate($templateKey), [
340
            'action' => 'edit',
341
            'form' => $formView,
342
            'object' => $existingObject,
343
            'objectId' => $objectId,
344
        ], null);
345
    }
346
347
    /**
348
     * Batch action.
349
     *
350
     * @return Response|RedirectResponse
351
     *
352
     * @throws NotFoundHttpException If the HTTP method is not POST
353
     * @throws \RuntimeException     If the batch action is not defined
354
     */
355
    public function batchAction()
356
    {
357
        $request = $this->getRequest();
358
        $restMethod = $this->getRestMethod();
359
360
        if ('POST' !== $restMethod) {
361
            throw $this->createNotFoundException(sprintf('Invalid request type "%s", POST expected', $restMethod));
362
        }
363
364
        // check the csrf token
365
        $this->validateCsrfToken('sonata.batch');
366
367
        $confirmation = $request->get('confirmation', false);
368
369
        if ($data = json_decode($request->get('data'), true)) {
370
            $action = $data['action'];
371
            $idx = $data['idx'];
372
            $allElements = $data['all_elements'];
373
            $request->request->replace(array_merge($request->request->all(), $data));
374
        } else {
375
            $request->request->set('idx', $request->get('idx', []));
376
            $request->request->set('all_elements', $request->get('all_elements', false));
377
378
            $action = $request->get('action');
379
            $idx = $request->get('idx');
380
            $allElements = $request->get('all_elements');
381
            $data = $request->request->all();
382
383
            unset($data['_sonata_csrf_token']);
384
        }
385
386
        // NEXT_MAJOR: Remove reflection check.
387
        $reflector = new \ReflectionMethod($this->admin, 'getBatchActions');
388
        if ($reflector->getDeclaringClass()->getName() === get_class($this->admin)) {
389
            @trigger_error('Override Sonata\AdminBundle\Admin\AbstractAdmin::getBatchActions method'
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
390
                .' is deprecated since version 3.2.'
391
                .' Use Sonata\AdminBundle\Admin\AbstractAdmin::configureBatchActions instead.'
392
                .' The method will be final in 4.0.', E_USER_DEPRECATED
393
            );
394
        }
395
        $batchActions = $this->admin->getBatchActions();
396
        if (!array_key_exists($action, $batchActions)) {
397
            throw new \RuntimeException(sprintf('The `%s` batch action is not defined', $action));
398
        }
399
400
        $camelizedAction = Inflector::classify($action);
401
        $isRelevantAction = sprintf('batchAction%sIsRelevant', $camelizedAction);
402
403
        if (method_exists($this, $isRelevantAction)) {
404
            $nonRelevantMessage = call_user_func([$this, $isRelevantAction], $idx, $allElements, $request);
405
        } else {
406
            $nonRelevantMessage = count($idx) != 0 || $allElements; // at least one item is selected
407
        }
408
409
        if (!$nonRelevantMessage) { // default non relevant message (if false of null)
410
            $nonRelevantMessage = 'flash_batch_empty';
411
        }
412
413
        $datagrid = $this->admin->getDatagrid();
414
        $datagrid->buildPager();
415
416
        if (true !== $nonRelevantMessage) {
417
            $this->addFlash(
418
                'sonata_flash_info',
419
                $this->trans($nonRelevantMessage, [], 'SonataAdminBundle')
420
            );
421
422
            return new RedirectResponse(
423
                $this->admin->generateUrl(
424
                    'list',
425
                    ['filter' => $this->admin->getFilterParameters()]
426
                )
427
            );
428
        }
429
430
        $askConfirmation = isset($batchActions[$action]['ask_confirmation']) ?
431
            $batchActions[$action]['ask_confirmation'] :
432
            true;
433
434
        if ($askConfirmation && $confirmation != 'ok') {
435
            $actionLabel = $batchActions[$action]['label'];
436
            $batchTranslationDomain = isset($batchActions[$action]['translation_domain']) ?
437
                $batchActions[$action]['translation_domain'] :
438
                $this->admin->getTranslationDomain();
439
440
            $formView = $datagrid->getForm()->createView();
441
            $this->setFormTheme($formView, $this->admin->getFilterTheme());
0 ignored issues
show
Documentation introduced by
$this->admin->getFilterTheme() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
442
443
            return $this->render($this->admin->getTemplate('batch_confirmation'), [
444
                'action' => 'list',
445
                'action_label' => $actionLabel,
446
                'batch_translation_domain' => $batchTranslationDomain,
447
                'datagrid' => $datagrid,
448
                'form' => $formView,
449
                'data' => $data,
450
                'csrf_token' => $this->getCsrfToken('sonata.batch'),
451
            ], null);
452
        }
453
454
        // execute the action, batchActionXxxxx
455
        $finalAction = sprintf('batchAction%s', $camelizedAction);
456
        if (!is_callable([$this, $finalAction])) {
457
            throw new \RuntimeException(sprintf('A `%s::%s` method must be callable', get_class($this), $finalAction));
458
        }
459
460
        $query = $datagrid->getQuery();
461
462
        $query->setFirstResult(null);
463
        $query->setMaxResults(null);
464
465
        $this->admin->preBatchAction($action, $query, $idx, $allElements);
466
467
        if (count($idx) > 0) {
468
            $this->admin->getModelManager()->addIdentifiersToQuery($this->admin->getClass(), $query, $idx);
469
        } elseif (!$allElements) {
470
            $query = null;
471
        }
472
473
        return call_user_func([$this, $finalAction], $query, $request);
474
    }
475
476
    /**
477
     * Create action.
478
     *
479
     * @return Response
480
     *
481
     * @throws AccessDeniedException If access is not granted
482
     */
483
    public function createAction()
484
    {
485
        $request = $this->getRequest();
486
        // the key used to lookup the template
487
        $templateKey = 'edit';
488
489
        $this->admin->checkAccess('create');
490
491
        $class = new \ReflectionClass($this->admin->hasActiveSubClass() ? $this->admin->getActiveSubClass() : $this->admin->getClass());
492
493
        if ($class->isAbstract()) {
494
            return $this->render(
495
                'SonataAdminBundle:CRUD:select_subclass.html.twig',
496
                [
497
                    'base_template' => $this->getBaseTemplate(),
498
                    'admin' => $this->admin,
499
                    'action' => 'create',
500
                ],
501
                null,
502
                $request
0 ignored issues
show
Unused Code introduced by
The call to CRUDController::render() has too many arguments starting with $request.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
503
            );
504
        }
505
506
        $newObject = $this->admin->getNewInstance();
507
508
        $preResponse = $this->preCreate($request, $newObject);
509
        if ($preResponse !== null) {
510
            return $preResponse;
511
        }
512
513
        $this->admin->setSubject($newObject);
514
515
        /** @var $form \Symfony\Component\Form\Form */
516
        $form = $this->admin->getForm();
517
        $form->setData($newObject);
518
        $form->handleRequest($request);
519
520
        if ($form->isSubmitted()) {
521
            //TODO: remove this check for 4.0
522
            if (method_exists($this->admin, 'preValidate')) {
523
                $this->admin->preValidate($newObject);
0 ignored issues
show
Bug introduced by
The method preValidate() does not exist on Sonata\AdminBundle\Admin\AdminInterface. Did you maybe mean validate()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
524
            }
525
            $isFormValid = $form->isValid();
526
527
            // persist if the form was valid and if in preview mode the preview was approved
528
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
529
                $submittedObject = $form->getData();
530
                $this->admin->setSubject($submittedObject);
531
                $this->admin->checkAccess('create', $submittedObject);
532
533
                try {
534
                    $newObject = $this->admin->create($submittedObject);
535
536
                    if ($this->isXmlHttpRequest()) {
537
                        return $this->renderJson([
538
                            'result' => 'ok',
539
                            'objectId' => $this->admin->getNormalizedIdentifier($newObject),
540
                        ], 200, []);
541
                    }
542
543
                    $this->addFlash(
544
                        'sonata_flash_success',
545
                        $this->trans(
546
                            'flash_create_success',
547
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
548
                            'SonataAdminBundle'
549
                        )
550
                    );
551
552
                    // redirect to edit mode
553
                    return $this->redirectTo($newObject);
554
                } catch (ModelManagerException $e) {
555
                    $this->handleModelManagerException($e);
556
557
                    $isFormValid = false;
558
                }
559
            }
560
561
            // show an error message if the form failed validation
562
            if (!$isFormValid) {
563
                if (!$this->isXmlHttpRequest()) {
564
                    $this->addFlash(
565
                        'sonata_flash_error',
566
                        $this->trans(
567
                            'flash_create_error',
568
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
569
                            'SonataAdminBundle'
570
                        )
571
                    );
572
                }
573
            } elseif ($this->isPreviewRequested()) {
574
                // pick the preview template if the form was valid and preview was requested
575
                $templateKey = 'preview';
576
                $this->admin->getShow();
577
            }
578
        }
579
580
        $formView = $form->createView();
581
        // set the theme for the current Admin Form
582
        $this->setFormTheme($formView, $this->admin->getFormTheme());
0 ignored issues
show
Documentation introduced by
$this->admin->getFormTheme() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
583
584
        return $this->render($this->admin->getTemplate($templateKey), [
585
            'action' => 'create',
586
            'form' => $formView,
587
            'object' => $newObject,
588
            'objectId' => null,
589
        ], null);
590
    }
591
592
    /**
593
     * Show action.
594
     *
595
     * @param int|string|null $id
596
     *
597
     * @return Response
598
     *
599
     * @throws NotFoundHttpException If the object does not exist
600
     * @throws AccessDeniedException If access is not granted
601
     */
602
    public function showAction($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
603
    {
604
        $request = $this->getRequest();
605
        $id = $this->getObjectIdFromRouteIdHandler();
606
607
        $object = $this->admin->getObject($id);
608
609
        if (!$object) {
610
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
611
        }
612
613
        $this->admin->checkAccess('show', $object);
614
615
        $preResponse = $this->preShow($request, $object);
616
        if ($preResponse !== null) {
617
            return $preResponse;
618
        }
619
620
        $this->admin->setSubject($object);
621
622
        return $this->render($this->admin->getTemplate('show'), [
623
            'action' => 'show',
624
            'object' => $object,
625
            'elements' => $this->admin->getShow(),
626
        ], null);
627
    }
628
629
    /**
630
     * Show history revisions for object.
631
     *
632
     * @param int|string|null $id
633
     *
634
     * @return Response
635
     *
636
     * @throws AccessDeniedException If access is not granted
637
     * @throws NotFoundHttpException If the object does not exist or the audit reader is not available
638
     */
639
    public function historyAction($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
640
    {
641
        $request = $this->getRequest();
0 ignored issues
show
Unused Code introduced by
$request is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
642
        $id = $this->getObjectIdFromRouteIdHandler();
643
644
        $object = $this->admin->getObject($id);
645
646
        if (!$object) {
647
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
648
        }
649
650
        $this->admin->checkAccess('history', $object);
651
652
        $manager = $this->get('sonata.admin.audit.manager');
653
654
        if (!$manager->hasReader($this->admin->getClass())) {
655
            throw $this->createNotFoundException(
656
                sprintf(
657
                    'unable to find the audit reader for class : %s',
658
                    $this->admin->getClass()
659
                )
660
            );
661
        }
662
663
        $reader = $manager->getReader($this->admin->getClass());
664
665
        $revisions = $reader->findRevisions($this->admin->getClass(), $id);
666
667
        return $this->render($this->admin->getTemplate('history'), [
668
            'action' => 'history',
669
            'object' => $object,
670
            'revisions' => $revisions,
671
            'currentRevision' => $revisions ? current($revisions) : false,
672
        ], null);
673
    }
674
675
    /**
676
     * View history revision of object.
677
     *
678
     * @param int|string|null $id
679
     * @param string|null     $revision
680
     *
681
     * @return Response
682
     *
683
     * @throws AccessDeniedException If access is not granted
684
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
685
     */
686
    public function historyViewRevisionAction($id = null, $revision = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
687
    {
688
        $request = $this->getRequest();
0 ignored issues
show
Unused Code introduced by
$request is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
689
        $id = $this->getObjectIdFromRouteIdHandler();
690
691
        $object = $this->admin->getObject($id);
692
693
        if (!$object) {
694
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
695
        }
696
697
        $this->admin->checkAccess('historyViewRevision', $object);
698
699
        $manager = $this->get('sonata.admin.audit.manager');
700
701
        if (!$manager->hasReader($this->admin->getClass())) {
702
            throw $this->createNotFoundException(
703
                sprintf(
704
                    'unable to find the audit reader for class : %s',
705
                    $this->admin->getClass()
706
                )
707
            );
708
        }
709
710
        $reader = $manager->getReader($this->admin->getClass());
711
712
        // retrieve the revisioned object
713
        $object = $reader->find($this->admin->getClass(), $id, $revision);
714
715
        if (!$object) {
716
            throw $this->createNotFoundException(
717
                sprintf(
718
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
719
                    $id,
720
                    $revision,
721
                    $this->admin->getClass()
722
                )
723
            );
724
        }
725
726
        $this->admin->setSubject($object);
727
728
        return $this->render($this->admin->getTemplate('show'), [
729
            'action' => 'show',
730
            'object' => $object,
731
            'elements' => $this->admin->getShow(),
732
        ], null);
733
    }
734
735
    /**
736
     * Compare history revisions of object.
737
     *
738
     * @param int|string|null $id
739
     * @param int|string|null $base_revision
740
     * @param int|string|null $compare_revision
741
     *
742
     * @return Response
743
     *
744
     * @throws AccessDeniedException If access is not granted
745
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
746
     */
747
    public function historyCompareRevisionsAction($id = null, $base_revision = null, $compare_revision = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
748
    {
749
        $request = $this->getRequest();
0 ignored issues
show
Unused Code introduced by
$request is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
750
751
        $this->admin->checkAccess('historyCompareRevisions');
752
753
        $id = $this->getObjectIdFromRouteIdHandler();
754
755
        $object = $this->admin->getObject($id);
756
757
        if (!$object) {
758
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
759
        }
760
761
        $manager = $this->get('sonata.admin.audit.manager');
762
763
        if (!$manager->hasReader($this->admin->getClass())) {
764
            throw $this->createNotFoundException(
765
                sprintf(
766
                    'unable to find the audit reader for class : %s',
767
                    $this->admin->getClass()
768
                )
769
            );
770
        }
771
772
        $reader = $manager->getReader($this->admin->getClass());
773
774
        // retrieve the base revision
775
        $base_object = $reader->find($this->admin->getClass(), $id, $base_revision);
776
        if (!$base_object) {
777
            throw $this->createNotFoundException(
778
                sprintf(
779
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
780
                    $id,
781
                    $base_revision,
782
                    $this->admin->getClass()
783
                )
784
            );
785
        }
786
787
        // retrieve the compare revision
788
        $compare_object = $reader->find($this->admin->getClass(), $id, $compare_revision);
789
        if (!$compare_object) {
790
            throw $this->createNotFoundException(
791
                sprintf(
792
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
793
                    $id,
794
                    $compare_revision,
795
                    $this->admin->getClass()
796
                )
797
            );
798
        }
799
800
        $this->admin->setSubject($base_object);
801
802
        return $this->render($this->admin->getTemplate('show_compare'), [
803
            'action' => 'show',
804
            'object' => $base_object,
805
            'object_compare' => $compare_object,
806
            'elements' => $this->admin->getShow(),
807
        ], null);
808
    }
809
810
    /**
811
     * Export data to specified format.
812
     *
813
     * @param Request $request
814
     *
815
     * @return Response
816
     *
817
     * @throws AccessDeniedException If access is not granted
818
     * @throws \RuntimeException     If the export format is invalid
819
     */
820
    public function exportAction(Request $request)
821
    {
822
        $this->admin->checkAccess('export');
823
824
        $format = $request->get('format');
825
826
        // NEXT_MAJOR: remove the check
827
        if (!$this->has('sonata.admin.admin_exporter')) {
828
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
829
                'Not registering the exporter bundle is deprecated since version 3.14.'
830
                .' You must register it to be able to use the export action in 4.0.',
831
                E_USER_DEPRECATED
832
            );
833
            $allowedExportFormats = (array) $this->admin->getExportFormats();
834
835
            $class = $this->admin->getClass();
836
            $filename = sprintf(
837
                'export_%s_%s.%s',
838
                strtolower(substr($class, strripos($class, '\\') + 1)),
839
                date('Y_m_d_H_i_s', strtotime('now')),
840
                $format
841
            );
842
            $exporter = $this->get('sonata.admin.exporter');
843
        } else {
844
            $adminExporter = $this->get('sonata.admin.admin_exporter');
845
            $allowedExportFormats = $adminExporter->getAvailableFormats($this->admin);
846
            $filename = $adminExporter->getExportFilename($this->admin, $format);
847
            $exporter = $this->get('sonata.exporter.exporter');
848
        }
849
850
        if (!in_array($format, $allowedExportFormats)) {
851
            throw new \RuntimeException(
852
                sprintf(
853
                    'Export in format `%s` is not allowed for class: `%s`. Allowed formats are: `%s`',
854
                    $format,
855
                    $this->admin->getClass(),
856
                    implode(', ', $allowedExportFormats)
857
                )
858
            );
859
        }
860
861
        return $exporter->getResponse(
862
            $format,
863
            $filename,
864
            $this->admin->getDataSourceIterator()
865
        );
866
    }
867
868
    /**
869
     * Returns the Response object associated to the acl action.
870
     *
871
     * @param int|string|null $id
872
     *
873
     * @return Response|RedirectResponse
874
     *
875
     * @throws AccessDeniedException If access is not granted
876
     * @throws NotFoundHttpException If the object does not exist or the ACL is not enabled
877
     */
878
    public function aclAction($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
879
    {
880
        $request = $this->getRequest();
881
882
        if (!$this->admin->isAclEnabled()) {
883
            throw $this->createNotFoundException('ACL are not enabled for this admin');
884
        }
885
886
        $id = $this->getObjectIdFromRouteIdHandler();
887
888
        $object = $this->admin->getObject($id);
889
890
        if (!$object) {
891
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
892
        }
893
894
        $this->admin->checkAccess('acl', $object);
895
896
        $this->admin->setSubject($object);
897
        $aclUsers = $this->getAclUsers();
898
        $aclRoles = $this->getAclRoles();
899
900
        $adminObjectAclManipulator = $this->get('sonata.admin.object.manipulator.acl.admin');
901
        $adminObjectAclData = new AdminObjectAclData(
902
            $this->admin,
903
            $object,
904
            $aclUsers,
905
            $adminObjectAclManipulator->getMaskBuilderClass(),
906
            $aclRoles
907
        );
908
909
        $aclUsersForm = $adminObjectAclManipulator->createAclUsersForm($adminObjectAclData);
910
        $aclRolesForm = $adminObjectAclManipulator->createAclRolesForm($adminObjectAclData);
911
912
        if ($request->getMethod() === 'POST') {
913
            if ($request->request->has(AdminObjectAclManipulator::ACL_USERS_FORM_NAME)) {
914
                $form = $aclUsersForm;
915
                $updateMethod = 'updateAclUsers';
916
            } elseif ($request->request->has(AdminObjectAclManipulator::ACL_ROLES_FORM_NAME)) {
917
                $form = $aclRolesForm;
918
                $updateMethod = 'updateAclRoles';
919
            }
920
921
            if (isset($form)) {
922
                $form->handleRequest($request);
923
924
                if ($form->isValid()) {
925
                    $adminObjectAclManipulator->$updateMethod($adminObjectAclData);
0 ignored issues
show
Bug introduced by
The variable $updateMethod does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
926
                    $this->addFlash(
927
                        'sonata_flash_success',
928
                        $this->trans('flash_acl_edit_success', [], 'SonataAdminBundle')
929
                    );
930
931
                    return new RedirectResponse($this->admin->generateObjectUrl('acl', $object));
932
                }
933
            }
934
        }
935
936
        return $this->render($this->admin->getTemplate('acl'), [
937
            'action' => 'acl',
938
            'permissions' => $adminObjectAclData->getUserPermissions(),
939
            'object' => $object,
940
            'users' => $aclUsers,
941
            'roles' => $aclRoles,
942
            'aclUsersForm' => $aclUsersForm->createView(),
943
            'aclRolesForm' => $aclRolesForm->createView(),
944
        ], null);
945
    }
946
947
    /**
948
     * @return Request
949
     */
950
    public function getRequest()
951
    {
952
        if ($this->container->has('request_stack')) {
953
            return $this->container->get('request_stack')->getCurrentRequest();
954
        }
955
956
        return $this->container->get('request');
957
    }
958
959
    /**
960
     * Render JSON.
961
     *
962
     * @param mixed $data
963
     * @param int   $status
964
     * @param array $headers
965
     *
966
     * @return Response with json encoded data
967
     */
968
    protected function renderJson($data, $status = 200, $headers = [])
969
    {
970
        return new JsonResponse($data, $status, $headers);
971
    }
972
973
    /**
974
     * Returns true if the request is a XMLHttpRequest.
975
     *
976
     * @return bool True if the request is an XMLHttpRequest, false otherwise
977
     */
978
    protected function isXmlHttpRequest()
979
    {
980
        $request = $this->getRequest();
981
982
        return $request->isXmlHttpRequest() || $request->get('_xml_http_request');
983
    }
984
985
    /**
986
     * Returns the correct RESTful verb, given either by the request itself or
987
     * via the "_method" parameter.
988
     *
989
     * @return string HTTP method, either
990
     */
991
    protected function getRestMethod()
992
    {
993
        $request = $this->getRequest();
994
995
        if (Request::getHttpMethodParameterOverride() || !$request->request->has('_method')) {
996
            return $request->getMethod();
997
        }
998
999
        return $request->request->get('_method');
1000
    }
1001
1002
    /**
1003
     * Contextualize the admin class depends on the current request.
1004
     *
1005
     * @throws \RuntimeException
1006
     */
1007
    protected function configure()
1008
    {
1009
        $request = $this->getRequest();
1010
1011
        $adminCode = $request->get('_sonata_admin');
1012
1013
        if (!$adminCode) {
1014
            throw new \RuntimeException(sprintf(
1015
                'There is no `_sonata_admin` defined for the controller `%s` and the current route `%s`',
1016
                get_class($this),
1017
                $request->get('_route')
1018
            ));
1019
        }
1020
1021
        $this->admin = $this->container->get('sonata.admin.pool')->getAdminByAdminCode($adminCode);
1022
1023
        if (!$this->admin) {
1024
            throw new \RuntimeException(sprintf(
1025
                'Unable to find the admin class related to the current controller (%s)',
1026
                get_class($this)
1027
            ));
1028
        }
1029
1030
        $rootAdmin = $this->admin;
1031
1032
        while ($rootAdmin->isChild()) {
1033
            $rootAdmin->setCurrentChild(true);
1034
            $rootAdmin = $rootAdmin->getParent();
1035
        }
1036
1037
        $rootAdmin->setRequest($request);
1038
1039
        if ($request->get('uniqid')) {
1040
            $this->admin->setUniqid($request->get('uniqid'));
1041
        }
1042
    }
1043
1044
    /**
1045
     * Proxy for the logger service of the container.
1046
     * If no such service is found, a NullLogger is returned.
1047
     *
1048
     * @return LoggerInterface
1049
     */
1050
    protected function getLogger()
1051
    {
1052
        if ($this->container->has('logger')) {
1053
            return $this->container->get('logger');
1054
        }
1055
1056
        return new NullLogger();
1057
    }
1058
1059
    /**
1060
     * Returns the base template name.
1061
     *
1062
     * @return string The template name
1063
     */
1064
    protected function getBaseTemplate()
1065
    {
1066
        if ($this->isXmlHttpRequest()) {
1067
            return $this->admin->getTemplate('ajax');
1068
        }
1069
1070
        return $this->admin->getTemplate('layout');
1071
    }
1072
1073
    /**
1074
     * @param \Exception $e
1075
     *
1076
     * @throws \Exception
1077
     */
1078
    protected function handleModelManagerException(\Exception $e)
1079
    {
1080
        if ($this->get('kernel')->isDebug()) {
1081
            throw $e;
1082
        }
1083
1084
        $context = ['exception' => $e];
1085
        if ($e->getPrevious()) {
1086
            $context['previous_exception_message'] = $e->getPrevious()->getMessage();
1087
        }
1088
        $this->getLogger()->error($e->getMessage(), $context);
1089
    }
1090
1091
    /**
1092
     * Redirect the user depend on this choice.
1093
     *
1094
     * @param object $object
1095
     *
1096
     * @return RedirectResponse
1097
     */
1098
    protected function redirectTo($object)
1099
    {
1100
        $request = $this->getRequest();
1101
1102
        $url = false;
1103
1104
        if (null !== $request->get('btn_update_and_list')) {
1105
            $url = $this->admin->generateUrl('list');
1106
        }
1107
        if (null !== $request->get('btn_create_and_list')) {
1108
            $url = $this->admin->generateUrl('list');
1109
        }
1110
1111
        if (null !== $request->get('btn_create_and_create')) {
1112
            $params = [];
1113
            if ($this->admin->hasActiveSubClass()) {
1114
                $params['subclass'] = $request->get('subclass');
1115
            }
1116
            $url = $this->admin->generateUrl('create', $params);
1117
        }
1118
1119
        if ($this->getRestMethod() === 'DELETE') {
1120
            $url = $this->admin->generateUrl('list');
1121
        }
1122
1123
        if (!$url) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $url of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1124
            foreach (['edit', 'show'] as $route) {
1125
                if ($this->admin->hasRoute($route) && $this->admin->hasAccess($route, $object)) {
1126
                    $url = $this->admin->generateObjectUrl($route, $object);
1127
1128
                    break;
1129
                }
1130
            }
1131
        }
1132
1133
        if (!$url) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $url of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1134
            $url = $this->admin->generateUrl('list');
1135
        }
1136
1137
        return new RedirectResponse($url);
1138
    }
1139
1140
    /**
1141
     * Returns true if the preview is requested to be shown.
1142
     *
1143
     * @return bool
1144
     */
1145
    protected function isPreviewRequested()
1146
    {
1147
        $request = $this->getRequest();
1148
1149
        return $request->get('btn_preview') !== null;
1150
    }
1151
1152
    /**
1153
     * Returns true if the preview has been approved.
1154
     *
1155
     * @return bool
1156
     */
1157
    protected function isPreviewApproved()
1158
    {
1159
        $request = $this->getRequest();
1160
1161
        return $request->get('btn_preview_approve') !== null;
1162
    }
1163
1164
    /**
1165
     * Returns true if the request is in the preview workflow.
1166
     *
1167
     * That means either a preview is requested or the preview has already been shown
1168
     * and it got approved/declined.
1169
     *
1170
     * @return bool
1171
     */
1172
    protected function isInPreviewMode()
1173
    {
1174
        return $this->admin->supportsPreviewMode()
1175
        && ($this->isPreviewRequested()
1176
            || $this->isPreviewApproved()
1177
            || $this->isPreviewDeclined());
1178
    }
1179
1180
    /**
1181
     * Returns true if the preview has been declined.
1182
     *
1183
     * @return bool
1184
     */
1185
    protected function isPreviewDeclined()
1186
    {
1187
        $request = $this->getRequest();
1188
1189
        return $request->get('btn_preview_decline') !== null;
1190
    }
1191
1192
    /**
1193
     * Gets ACL users.
1194
     *
1195
     * @return \Traversable
1196
     */
1197
    protected function getAclUsers()
1198
    {
1199
        $aclUsers = [];
1200
1201
        $userManagerServiceName = $this->container->getParameter('sonata.admin.security.acl_user_manager');
1202
        if ($userManagerServiceName !== null && $this->has($userManagerServiceName)) {
1203
            $userManager = $this->get($userManagerServiceName);
1204
1205
            if (method_exists($userManager, 'findUsers')) {
1206
                $aclUsers = $userManager->findUsers();
1207
            }
1208
        }
1209
1210
        return is_array($aclUsers) ? new \ArrayIterator($aclUsers) : $aclUsers;
1211
    }
1212
1213
    /**
1214
     * Gets ACL roles.
1215
     *
1216
     * @return \Traversable
1217
     */
1218
    protected function getAclRoles()
1219
    {
1220
        $aclRoles = [];
1221
        $roleHierarchy = $this->container->getParameter('security.role_hierarchy.roles');
1222
        $pool = $this->container->get('sonata.admin.pool');
1223
1224
        foreach ($pool->getAdminServiceIds() as $id) {
1225
            try {
1226
                $admin = $pool->getInstance($id);
1227
            } catch (\Exception $e) {
1228
                continue;
1229
            }
1230
1231
            $baseRole = $admin->getSecurityHandler()->getBaseRole($admin);
1232
            foreach ($admin->getSecurityInformation() as $role => $permissions) {
1233
                $role = sprintf($baseRole, $role);
1234
                $aclRoles[] = $role;
1235
            }
1236
        }
1237
1238
        foreach ($roleHierarchy as $name => $roles) {
1239
            $aclRoles[] = $name;
1240
            $aclRoles = array_merge($aclRoles, $roles);
1241
        }
1242
1243
        $aclRoles = array_unique($aclRoles);
1244
1245
        return is_array($aclRoles) ? new \ArrayIterator($aclRoles) : $aclRoles;
1246
    }
1247
1248
    /**
1249
     * Adds a flash message for type.
1250
     *
1251
     * @param string $type
1252
     * @param string $message
1253
     *
1254
     * @TODO Remove this method when bumping requirements to Symfony >= 2.6
1255
     */
1256
    protected function addFlash($type, $message)
1257
    {
1258
        if (method_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller', 'addFlash')) {
1259
            parent::addFlash($type, $message);
1260
        } else {
1261
            $this->get('session')
1262
                ->getFlashBag()
1263
                ->add($type, $message);
1264
        }
1265
    }
1266
1267
    /**
1268
     * Validate CSRF token for action without form.
1269
     *
1270
     * @param string $intention
1271
     *
1272
     * @throws HttpException
1273
     */
1274
    protected function validateCsrfToken($intention)
1275
    {
1276
        $request = $this->getRequest();
1277
        $token = $request->request->get('_sonata_csrf_token', false);
1278
1279
        if ($this->container->has('security.csrf.token_manager')) { // SF3.0
1280
            $valid = $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($intention, $token));
1281
        } elseif ($this->container->has('form.csrf_provider')) { // < SF3.0
1282
            $valid = $this->container->get('form.csrf_provider')->isCsrfTokenValid($intention, $token);
1283
        } else {
1284
            return;
1285
        }
1286
1287
        if (!$valid) {
1288
            throw new HttpException(400, 'The csrf token is not valid, CSRF attack?');
1289
        }
1290
    }
1291
1292
    /**
1293
     * Escape string for html output.
1294
     *
1295
     * @param string $s
1296
     *
1297
     * @return string
1298
     */
1299
    protected function escapeHtml($s)
1300
    {
1301
        return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1302
    }
1303
1304
    /**
1305
     * Get CSRF token.
1306
     *
1307
     * @param string $intention
1308
     *
1309
     * @return string|false
1310
     */
1311
    protected function getCsrfToken($intention)
1312
    {
1313
        if ($this->container->has('security.csrf.token_manager')) {
1314
            return $this->container->get('security.csrf.token_manager')->getToken($intention)->getValue();
1315
        }
1316
1317
        // TODO: Remove it when bumping requirements to SF 2.4+
1318
        if ($this->container->has('form.csrf_provider')) {
1319
            return $this->container->get('form.csrf_provider')->generateCsrfToken($intention);
1320
        }
1321
1322
        return false;
1323
    }
1324
1325
    /**
1326
     * This method can be overloaded in your custom CRUD controller.
1327
     * It's called from createAction.
1328
     *
1329
     * @param Request $request
1330
     * @param mixed   $object
1331
     *
1332
     * @return Response|null
1333
     */
1334
    protected function preCreate(Request $request, $object)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1335
    {
1336
    }
1337
1338
    /**
1339
     * This method can be overloaded in your custom CRUD controller.
1340
     * It's called from editAction.
1341
     *
1342
     * @param Request $request
1343
     * @param mixed   $object
1344
     *
1345
     * @return Response|null
1346
     */
1347
    protected function preEdit(Request $request, $object)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1348
    {
1349
    }
1350
1351
    /**
1352
     * This method can be overloaded in your custom CRUD controller.
1353
     * It's called from deleteAction.
1354
     *
1355
     * @param Request $request
1356
     * @param mixed   $object
1357
     *
1358
     * @return Response|null
1359
     */
1360
    protected function preDelete(Request $request, $object)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1361
    {
1362
    }
1363
1364
    /**
1365
     * This method can be overloaded in your custom CRUD controller.
1366
     * It's called from showAction.
1367
     *
1368
     * @param Request $request
1369
     * @param mixed   $object
1370
     *
1371
     * @return Response|null
1372
     */
1373
    protected function preShow(Request $request, $object)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1374
    {
1375
    }
1376
1377
    /**
1378
     * This method can be overloaded in your custom CRUD controller.
1379
     * It's called from listAction.
1380
     *
1381
     * @param Request $request
1382
     *
1383
     * @return Response|null
1384
     */
1385
    protected function preList(Request $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1386
    {
1387
    }
1388
1389
    /**
1390
     * Translate a message id.
1391
     *
1392
     * @param string $id
1393
     * @param array  $parameters
1394
     * @param string $domain
1395
     * @param string $locale
1396
     *
1397
     * @return string translated string
1398
     */
1399
    final protected function trans($id, array $parameters = [], $domain = null, $locale = null)
1400
    {
1401
        $domain = $domain ?: $this->admin->getTranslationDomain();
1402
1403
        return $this->get('translator')->trans($id, $parameters, $domain, $locale);
1404
    }
1405
1406
    /**
1407
     * @return int|string
1408
     */
1409
    protected function getObjectIdFromRouteIdHandler()
1410
    {
1411
        return $this->get('sonata.admin.route_id_handler')
1412
            ->getIdFromRequest($this->getRequest(), $this->admin);
1413
    }
1414
1415
    /**
1416
     * Sets the admin form theme to form view. Used for compatibility between Symfony versions.
1417
     *
1418
     * @param FormView $formView
1419
     * @param string   $theme
1420
     */
1421
    private function setFormTheme(FormView $formView, $theme)
1422
    {
1423
        $twig = $this->get('twig');
1424
1425
        try {
1426
            $twig
1427
                ->getRuntime('Symfony\Bridge\Twig\Form\TwigRenderer')
1428
                ->setTheme($formView, $theme);
1429
        } catch (\Twig_Error_Runtime $e) {
1430
            // BC for Symfony < 3.2 where this runtime not exists
1431
            $twig
1432
                ->getExtension('Symfony\Bridge\Twig\Extension\FormExtension')
1433
                ->renderer
1434
                ->setTheme($formView, $theme);
1435
        }
1436
    }
1437
}
1438