Completed
Push — master ( 602d8f...082e92 )
by Sullivan
05:29 queued 01:38
created

CRUDController::validateCsrfToken()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.2
c 0
b 0
f 0
cc 4
eloc 11
nc 5
nop 1
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\Bridge\Twig\AppVariable;
24
use Symfony\Bridge\Twig\Command\DebugCommand;
25
use Symfony\Bridge\Twig\Extension\FormExtension;
26
use Symfony\Bridge\Twig\Form\TwigRenderer;
27
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
28
use Symfony\Component\DependencyInjection\ContainerInterface;
29
use Symfony\Component\Form\FormRenderer;
30
use Symfony\Component\Form\FormView;
31
use Symfony\Component\HttpFoundation\JsonResponse;
32
use Symfony\Component\HttpFoundation\RedirectResponse;
33
use Symfony\Component\HttpFoundation\Request;
34
use Symfony\Component\HttpFoundation\Response;
35
use Symfony\Component\HttpKernel\Exception\HttpException;
36
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
37
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
38
use Symfony\Component\Security\Csrf\CsrfToken;
39
40
/**
41
 * @author Thomas Rabaix <[email protected]>
42
 */
43
class CRUDController extends Controller
44
{
45
    /**
46
     * The related Admin class.
47
     *
48
     * @var AdminInterface
49
     */
50
    protected $admin;
51
52
    /**
53
     * Sets the Container associated with this Controller.
54
     *
55
     * @param ContainerInterface $container A ContainerInterface instance
56
     */
57
    public function setContainer(ContainerInterface $container = null)
58
    {
59
        $this->container = $container;
60
61
        $this->configure();
62
    }
63
64
    /**
65
     * Adds mandatory parameters before calling render().
66
     */
67
    public function renderWithExtraParams($view, array $parameters = [], Response $response = null)
68
    {
69
        if (!$this->isXmlHttpRequest()) {
70
            $parameters['breadcrumbs_builder'] = $this->get('sonata.admin.breadcrumbs_builder');
71
        }
72
        $parameters['admin'] = isset($parameters['admin']) ?
73
            $parameters['admin'] :
74
            $this->admin;
75
76
        $parameters['base_template'] = isset($parameters['base_template']) ?
77
            $parameters['base_template'] :
78
            $this->getBaseTemplate();
79
80
        $parameters['admin_pool'] = $this->get('sonata.admin.pool');
81
82
        return $this->render($view, $parameters, $response);
83
    }
84
85
    /**
86
     * List action.
87
     *
88
     * @return Response
89
     *
90
     * @throws AccessDeniedException If access is not granted
91
     */
92
    public function listAction()
93
    {
94
        $request = $this->getRequest();
95
96
        $this->admin->checkAccess('list');
97
98
        $preResponse = $this->preList($request);
99
        if (null !== $preResponse) {
100
            return $preResponse;
101
        }
102
103
        if ($listMode = $request->get('_list_mode')) {
104
            $this->admin->setListMode($listMode);
105
        }
106
107
        $datagrid = $this->admin->getDatagrid();
108
        $formView = $datagrid->getForm()->createView();
109
110
        // set the theme for the current Admin Form
111
        $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...
112
113
        return $this->renderWithExtraParams($this->admin->getTemplate('list'), [
114
            'action' => 'list',
115
            'form' => $formView,
116
            'datagrid' => $datagrid,
117
            'csrf_token' => $this->getCsrfToken('sonata.batch'),
118
            'export_formats' => $this->has('sonata.admin.admin_exporter') ?
119
                $this->get('sonata.admin.admin_exporter')->getAvailableFormats($this->admin) :
120
                $this->admin->getExportFormats(),
121
        ], null);
122
    }
123
124
    /**
125
     * Execute a batch delete.
126
     *
127
     * @param ProxyQueryInterface $query
128
     *
129
     * @return RedirectResponse
130
     *
131
     * @throws AccessDeniedException If access is not granted
132
     */
133
    public function batchActionDelete(ProxyQueryInterface $query)
134
    {
135
        $this->admin->checkAccess('batchDelete');
136
137
        $modelManager = $this->admin->getModelManager();
138
139
        try {
140
            $modelManager->batchDelete($this->admin->getClass(), $query);
141
            $this->addFlash(
142
                'sonata_flash_success',
143
                $this->trans('flash_batch_delete_success', [], 'SonataAdminBundle')
144
            );
145
        } catch (ModelManagerException $e) {
146
            $this->handleModelManagerException($e);
147
            $this->addFlash(
148
                'sonata_flash_error',
149
                $this->trans('flash_batch_delete_error', [], 'SonataAdminBundle')
150
            );
151
        }
152
153
        return new RedirectResponse($this->admin->generateUrl(
154
            'list',
155
            ['filter' => $this->admin->getFilterParameters()]
156
        ));
157
    }
158
159
    /**
160
     * Delete action.
161
     *
162
     * @param int|string|null $id
163
     *
164
     * @return Response|RedirectResponse
165
     *
166
     * @throws NotFoundHttpException If the object does not exist
167
     * @throws AccessDeniedException If access is not granted
168
     */
169
    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...
170
    {
171
        $request = $this->getRequest();
172
        $id = $request->get($this->admin->getIdParameter());
173
        $object = $this->admin->getObject($id);
174
175
        if (!$object) {
176
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
177
        }
178
179
        $this->admin->checkAccess('delete', $object);
180
181
        $preResponse = $this->preDelete($request, $object);
182
        if (null !== $preResponse) {
183
            return $preResponse;
184
        }
185
186
        if ('DELETE' == $this->getRestMethod()) {
187
            // check the csrf token
188
            $this->validateCsrfToken('sonata.delete');
189
190
            $objectName = $this->admin->toString($object);
191
192
            try {
193
                $this->admin->delete($object);
194
195
                if ($this->isXmlHttpRequest()) {
196
                    return $this->renderJson(['result' => 'ok'], 200, []);
197
                }
198
199
                $this->addFlash(
200
                    'sonata_flash_success',
201
                    $this->trans(
202
                        'flash_delete_success',
203
                        ['%name%' => $this->escapeHtml($objectName)],
204
                        'SonataAdminBundle'
205
                    )
206
                );
207
            } catch (ModelManagerException $e) {
208
                $this->handleModelManagerException($e);
209
210
                if ($this->isXmlHttpRequest()) {
211
                    return $this->renderJson(['result' => 'error'], 200, []);
212
                }
213
214
                $this->addFlash(
215
                    'sonata_flash_error',
216
                    $this->trans(
217
                        'flash_delete_error',
218
                        ['%name%' => $this->escapeHtml($objectName)],
219
                        'SonataAdminBundle'
220
                    )
221
                );
222
            }
223
224
            return $this->redirectTo($object);
225
        }
226
227
        return $this->renderWithExtraParams($this->admin->getTemplate('delete'), [
228
            'object' => $object,
229
            'action' => 'delete',
230
            'csrf_token' => $this->getCsrfToken('sonata.delete'),
231
        ], null);
232
    }
233
234
    /**
235
     * Edit action.
236
     *
237
     * @param int|string|null $id
238
     *
239
     * @return Response|RedirectResponse
240
     *
241
     * @throws NotFoundHttpException If the object does not exist
242
     * @throws AccessDeniedException If access is not granted
243
     */
244
    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...
245
    {
246
        $request = $this->getRequest();
247
        // the key used to lookup the template
248
        $templateKey = 'edit';
249
250
        $id = $request->get($this->admin->getIdParameter());
251
        $existingObject = $this->admin->getObject($id);
252
253
        if (!$existingObject) {
254
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
255
        }
256
257
        $this->admin->checkAccess('edit', $existingObject);
258
259
        $preResponse = $this->preEdit($request, $existingObject);
260
        if (null !== $preResponse) {
261
            return $preResponse;
262
        }
263
264
        $this->admin->setSubject($existingObject);
265
        $objectId = $this->admin->getNormalizedIdentifier($existingObject);
266
267
        /** @var $form Form */
268
        $form = $this->admin->getForm();
269
        $form->setData($existingObject);
270
        $form->handleRequest($request);
271
272
        if ($form->isSubmitted()) {
273
            $this->admin->preValidate($existingObject);
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->renderWithExtraParams($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
        $batchActions = $this->admin->getBatchActions();
387
        if (!array_key_exists($action, $batchActions)) {
388
            throw new \RuntimeException(sprintf('The `%s` batch action is not defined', $action));
389
        }
390
391
        $camelizedAction = Inflector::classify($action);
392
        $isRelevantAction = sprintf('batchAction%sIsRelevant', ucfirst($camelizedAction));
393
394
        if (method_exists($this, $isRelevantAction)) {
395
            $nonRelevantMessage = call_user_func([$this, $isRelevantAction], $idx, $allElements, $request);
396
        } else {
397
            $nonRelevantMessage = 0 != count($idx) || $allElements; // at least one item is selected
398
        }
399
400
        if (!$nonRelevantMessage) { // default non relevant message (if false of null)
401
            $nonRelevantMessage = 'flash_batch_empty';
402
        }
403
404
        $datagrid = $this->admin->getDatagrid();
405
        $datagrid->buildPager();
406
407
        if (true !== $nonRelevantMessage) {
408
            $this->addFlash(
409
                'sonata_flash_info',
410
                $this->trans($nonRelevantMessage, [], 'SonataAdminBundle')
411
            );
412
413
            return new RedirectResponse(
414
                $this->admin->generateUrl(
415
                    'list',
416
                    ['filter' => $this->admin->getFilterParameters()]
417
                )
418
            );
419
        }
420
421
        $askConfirmation = isset($batchActions[$action]['ask_confirmation']) ?
422
            $batchActions[$action]['ask_confirmation'] :
423
            true;
424
425
        if ($askConfirmation && 'ok' != $confirmation) {
426
            $actionLabel = $batchActions[$action]['label'];
427
            $batchTranslationDomain = isset($batchActions[$action]['translation_domain']) ?
428
                $batchActions[$action]['translation_domain'] :
429
                $this->admin->getTranslationDomain();
430
431
            $formView = $datagrid->getForm()->createView();
432
            $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...
433
434
            return $this->renderWithExtraParams($this->admin->getTemplate('batch_confirmation'), [
435
                'action' => 'list',
436
                'action_label' => $actionLabel,
437
                'batch_translation_domain' => $batchTranslationDomain,
438
                'datagrid' => $datagrid,
439
                'form' => $formView,
440
                'data' => $data,
441
                'csrf_token' => $this->getCsrfToken('sonata.batch'),
442
            ], null);
443
        }
444
445
        // execute the action, batchActionXxxxx
446
        $finalAction = sprintf('batchAction%s', $camelizedAction);
447
        if (!is_callable([$this, $finalAction])) {
448
            throw new \RuntimeException(sprintf('A `%s::%s` method must be callable', get_class($this), $finalAction));
449
        }
450
451
        $query = $datagrid->getQuery();
452
453
        $query->setFirstResult(null);
454
        $query->setMaxResults(null);
455
456
        $this->admin->preBatchAction($action, $query, $idx, $allElements);
457
458
        if (count($idx) > 0) {
459
            $this->admin->getModelManager()->addIdentifiersToQuery($this->admin->getClass(), $query, $idx);
460
        } elseif (!$allElements) {
461
            $query = null;
462
        }
463
464
        return call_user_func([$this, $finalAction], $query, $request);
465
    }
466
467
    /**
468
     * Create action.
469
     *
470
     * @return Response
471
     *
472
     * @throws AccessDeniedException If access is not granted
473
     */
474
    public function createAction()
475
    {
476
        $request = $this->getRequest();
477
        // the key used to lookup the template
478
        $templateKey = 'edit';
479
480
        $this->admin->checkAccess('create');
481
482
        $class = new \ReflectionClass($this->admin->hasActiveSubClass() ? $this->admin->getActiveSubClass() : $this->admin->getClass());
483
484
        if ($class->isAbstract()) {
485
            return $this->renderWithExtraParams(
486
                'SonataAdminBundle:CRUD:select_subclass.html.twig',
487
                [
488
                    'base_template' => $this->getBaseTemplate(),
489
                    'admin' => $this->admin,
490
                    'action' => 'create',
491
                ],
492
                null
493
            );
494
        }
495
496
        $newObject = $this->admin->getNewInstance();
497
498
        $preResponse = $this->preCreate($request, $newObject);
499
        if (null !== $preResponse) {
500
            return $preResponse;
501
        }
502
503
        $this->admin->setSubject($newObject);
504
505
        /** @var $form \Symfony\Component\Form\Form */
506
        $form = $this->admin->getForm();
507
        $form->setData($newObject);
508
        $form->handleRequest($request);
509
510
        if ($form->isSubmitted()) {
511
            $this->admin->preValidate($newObject);
512
            $isFormValid = $form->isValid();
513
514
            // persist if the form was valid and if in preview mode the preview was approved
515
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
516
                $submittedObject = $form->getData();
517
                $this->admin->setSubject($submittedObject);
518
                $this->admin->checkAccess('create', $submittedObject);
519
520
                try {
521
                    $newObject = $this->admin->create($submittedObject);
522
523
                    if ($this->isXmlHttpRequest()) {
524
                        return $this->renderJson([
525
                            'result' => 'ok',
526
                            'objectId' => $this->admin->getNormalizedIdentifier($newObject),
527
                        ], 200, []);
528
                    }
529
530
                    $this->addFlash(
531
                        'sonata_flash_success',
532
                        $this->trans(
533
                            'flash_create_success',
534
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
535
                            'SonataAdminBundle'
536
                        )
537
                    );
538
539
                    // redirect to edit mode
540
                    return $this->redirectTo($newObject);
541
                } catch (ModelManagerException $e) {
542
                    $this->handleModelManagerException($e);
543
544
                    $isFormValid = false;
545
                }
546
            }
547
548
            // show an error message if the form failed validation
549
            if (!$isFormValid) {
550
                if (!$this->isXmlHttpRequest()) {
551
                    $this->addFlash(
552
                        'sonata_flash_error',
553
                        $this->trans(
554
                            'flash_create_error',
555
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
556
                            'SonataAdminBundle'
557
                        )
558
                    );
559
                }
560
            } elseif ($this->isPreviewRequested()) {
561
                // pick the preview template if the form was valid and preview was requested
562
                $templateKey = 'preview';
563
                $this->admin->getShow();
564
            }
565
        }
566
567
        $formView = $form->createView();
568
        // set the theme for the current Admin Form
569
        $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...
570
571
        return $this->renderWithExtraParams($this->admin->getTemplate($templateKey), [
572
            'action' => 'create',
573
            'form' => $formView,
574
            'object' => $newObject,
575
            'objectId' => null,
576
        ], null);
577
    }
578
579
    /**
580
     * Show action.
581
     *
582
     * @param int|string|null $id
583
     *
584
     * @return Response
585
     *
586
     * @throws NotFoundHttpException If the object does not exist
587
     * @throws AccessDeniedException If access is not granted
588
     */
589
    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...
590
    {
591
        $request = $this->getRequest();
592
        $id = $request->get($this->admin->getIdParameter());
593
594
        $object = $this->admin->getObject($id);
595
596
        if (!$object) {
597
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
598
        }
599
600
        $this->admin->checkAccess('show', $object);
601
602
        $preResponse = $this->preShow($request, $object);
603
        if (null !== $preResponse) {
604
            return $preResponse;
605
        }
606
607
        $this->admin->setSubject($object);
608
609
        return $this->renderWithExtraParams($this->admin->getTemplate('show'), [
610
            'action' => 'show',
611
            'object' => $object,
612
            'elements' => $this->admin->getShow(),
613
        ], null);
614
    }
615
616
    /**
617
     * Show history revisions for object.
618
     *
619
     * @param int|string|null $id
620
     *
621
     * @return Response
622
     *
623
     * @throws AccessDeniedException If access is not granted
624
     * @throws NotFoundHttpException If the object does not exist or the audit reader is not available
625
     */
626
    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...
627
    {
628
        $request = $this->getRequest();
629
        $id = $request->get($this->admin->getIdParameter());
630
631
        $object = $this->admin->getObject($id);
632
633
        if (!$object) {
634
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
635
        }
636
637
        $this->admin->checkAccess('history', $object);
638
639
        $manager = $this->get('sonata.admin.audit.manager');
640
641
        if (!$manager->hasReader($this->admin->getClass())) {
642
            throw $this->createNotFoundException(
643
                sprintf(
644
                    'unable to find the audit reader for class : %s',
645
                    $this->admin->getClass()
646
                )
647
            );
648
        }
649
650
        $reader = $manager->getReader($this->admin->getClass());
651
652
        $revisions = $reader->findRevisions($this->admin->getClass(), $id);
653
654
        return $this->renderWithExtraParams($this->admin->getTemplate('history'), [
655
            'action' => 'history',
656
            'object' => $object,
657
            'revisions' => $revisions,
658
            'currentRevision' => $revisions ? current($revisions) : false,
659
        ], null);
660
    }
661
662
    /**
663
     * View history revision of object.
664
     *
665
     * @param int|string|null $id
666
     * @param string|null     $revision
667
     *
668
     * @return Response
669
     *
670
     * @throws AccessDeniedException If access is not granted
671
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
672
     */
673
    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...
674
    {
675
        $request = $this->getRequest();
676
        $id = $request->get($this->admin->getIdParameter());
677
678
        $object = $this->admin->getObject($id);
679
680
        if (!$object) {
681
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
682
        }
683
684
        $this->admin->checkAccess('historyViewRevision', $object);
685
686
        $manager = $this->get('sonata.admin.audit.manager');
687
688
        if (!$manager->hasReader($this->admin->getClass())) {
689
            throw $this->createNotFoundException(
690
                sprintf(
691
                    'unable to find the audit reader for class : %s',
692
                    $this->admin->getClass()
693
                )
694
            );
695
        }
696
697
        $reader = $manager->getReader($this->admin->getClass());
698
699
        // retrieve the revisioned object
700
        $object = $reader->find($this->admin->getClass(), $id, $revision);
701
702
        if (!$object) {
703
            throw $this->createNotFoundException(
704
                sprintf(
705
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
706
                    $id,
707
                    $revision,
708
                    $this->admin->getClass()
709
                )
710
            );
711
        }
712
713
        $this->admin->setSubject($object);
714
715
        return $this->renderWithExtraParams($this->admin->getTemplate('show'), [
716
            'action' => 'show',
717
            'object' => $object,
718
            'elements' => $this->admin->getShow(),
719
        ], null);
720
    }
721
722
    /**
723
     * Compare history revisions of object.
724
     *
725
     * @param int|string|null $id
726
     * @param int|string|null $base_revision
727
     * @param int|string|null $compare_revision
728
     *
729
     * @return Response
730
     *
731
     * @throws AccessDeniedException If access is not granted
732
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
733
     */
734
    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...
735
    {
736
        $request = $this->getRequest();
737
738
        $this->admin->checkAccess('historyCompareRevisions');
739
740
        $id = $request->get($this->admin->getIdParameter());
741
742
        $object = $this->admin->getObject($id);
743
744
        if (!$object) {
745
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
746
        }
747
748
        $manager = $this->get('sonata.admin.audit.manager');
749
750
        if (!$manager->hasReader($this->admin->getClass())) {
751
            throw $this->createNotFoundException(
752
                sprintf(
753
                    'unable to find the audit reader for class : %s',
754
                    $this->admin->getClass()
755
                )
756
            );
757
        }
758
759
        $reader = $manager->getReader($this->admin->getClass());
760
761
        // retrieve the base revision
762
        $base_object = $reader->find($this->admin->getClass(), $id, $base_revision);
763
        if (!$base_object) {
764
            throw $this->createNotFoundException(
765
                sprintf(
766
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
767
                    $id,
768
                    $base_revision,
769
                    $this->admin->getClass()
770
                )
771
            );
772
        }
773
774
        // retrieve the compare revision
775
        $compare_object = $reader->find($this->admin->getClass(), $id, $compare_revision);
776
        if (!$compare_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
                    $compare_revision,
782
                    $this->admin->getClass()
783
                )
784
            );
785
        }
786
787
        $this->admin->setSubject($base_object);
788
789
        return $this->renderWithExtraParams($this->admin->getTemplate('show_compare'), [
790
            'action' => 'show',
791
            'object' => $base_object,
792
            'object_compare' => $compare_object,
793
            'elements' => $this->admin->getShow(),
794
        ], null);
795
    }
796
797
    /**
798
     * Export data to specified format.
799
     *
800
     * @param Request $request
801
     *
802
     * @return Response
803
     *
804
     * @throws AccessDeniedException If access is not granted
805
     * @throws \RuntimeException     If the export format is invalid
806
     */
807
    public function exportAction(Request $request)
808
    {
809
        $this->admin->checkAccess('export');
810
811
        $format = $request->get('format');
812
813
        // NEXT_MAJOR: remove the check
814
        if (!$this->has('sonata.admin.admin_exporter')) {
815
            @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...
816
                'Not registering the exporter bundle is deprecated since version 3.14.'
817
                .' You must register it to be able to use the export action in 4.0.',
818
                E_USER_DEPRECATED
819
            );
820
            $allowedExportFormats = (array) $this->admin->getExportFormats();
821
822
            $class = $this->admin->getClass();
823
            $filename = sprintf(
824
                'export_%s_%s.%s',
825
                strtolower(substr($class, strripos($class, '\\') + 1)),
826
                date('Y_m_d_H_i_s', strtotime('now')),
827
                $format
828
            );
829
            $exporter = $this->get('sonata.admin.exporter');
830
        } else {
831
            $adminExporter = $this->get('sonata.admin.admin_exporter');
832
            $allowedExportFormats = $adminExporter->getAvailableFormats($this->admin);
833
            $filename = $adminExporter->getExportFilename($this->admin, $format);
834
            $exporter = $this->get('sonata.exporter.exporter');
835
        }
836
837
        if (!in_array($format, $allowedExportFormats)) {
838
            throw new \RuntimeException(
839
                sprintf(
840
                    'Export in format `%s` is not allowed for class: `%s`. Allowed formats are: `%s`',
841
                    $format,
842
                    $this->admin->getClass(),
843
                    implode(', ', $allowedExportFormats)
844
                )
845
            );
846
        }
847
848
        return $exporter->getResponse(
849
            $format,
850
            $filename,
851
            $this->admin->getDataSourceIterator()
852
        );
853
    }
854
855
    /**
856
     * Returns the Response object associated to the acl action.
857
     *
858
     * @param int|string|null $id
859
     *
860
     * @return Response|RedirectResponse
861
     *
862
     * @throws AccessDeniedException If access is not granted
863
     * @throws NotFoundHttpException If the object does not exist or the ACL is not enabled
864
     */
865
    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...
866
    {
867
        $request = $this->getRequest();
868
869
        if (!$this->admin->isAclEnabled()) {
870
            throw $this->createNotFoundException('ACL are not enabled for this admin');
871
        }
872
873
        $id = $request->get($this->admin->getIdParameter());
874
875
        $object = $this->admin->getObject($id);
876
877
        if (!$object) {
878
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
879
        }
880
881
        $this->admin->checkAccess('acl', $object);
882
883
        $this->admin->setSubject($object);
884
        $aclUsers = $this->getAclUsers();
885
        $aclRoles = $this->getAclRoles();
886
887
        $adminObjectAclManipulator = $this->get('sonata.admin.object.manipulator.acl.admin');
888
        $adminObjectAclData = new AdminObjectAclData(
889
            $this->admin,
890
            $object,
891
            $aclUsers,
892
            $adminObjectAclManipulator->getMaskBuilderClass(),
893
            $aclRoles
894
        );
895
896
        $aclUsersForm = $adminObjectAclManipulator->createAclUsersForm($adminObjectAclData);
897
        $aclRolesForm = $adminObjectAclManipulator->createAclRolesForm($adminObjectAclData);
898
899
        if ('POST' === $request->getMethod()) {
900
            if ($request->request->has(AdminObjectAclManipulator::ACL_USERS_FORM_NAME)) {
901
                $form = $aclUsersForm;
902
                $updateMethod = 'updateAclUsers';
903
            } elseif ($request->request->has(AdminObjectAclManipulator::ACL_ROLES_FORM_NAME)) {
904
                $form = $aclRolesForm;
905
                $updateMethod = 'updateAclRoles';
906
            }
907
908
            if (isset($form)) {
909
                $form->handleRequest($request);
910
911
                if ($form->isValid()) {
912
                    $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...
913
                    $this->addFlash(
914
                        'sonata_flash_success',
915
                        $this->trans('flash_acl_edit_success', [], 'SonataAdminBundle')
916
                    );
917
918
                    return new RedirectResponse($this->admin->generateObjectUrl('acl', $object));
919
                }
920
            }
921
        }
922
923
        return $this->renderWithExtraParams($this->admin->getTemplate('acl'), [
924
            'action' => 'acl',
925
            'permissions' => $adminObjectAclData->getUserPermissions(),
926
            'object' => $object,
927
            'users' => $aclUsers,
928
            'roles' => $aclRoles,
929
            'aclUsersForm' => $aclUsersForm->createView(),
930
            'aclRolesForm' => $aclRolesForm->createView(),
931
        ], null);
932
    }
933
934
    /**
935
     * @return Request
936
     */
937
    public function getRequest()
938
    {
939
        if ($this->container->has('request_stack')) {
940
            return $this->container->get('request_stack')->getCurrentRequest();
941
        }
942
943
        return $this->container->get('request');
944
    }
945
946
    /**
947
     * Render JSON.
948
     *
949
     * @param mixed $data
950
     * @param int   $status
951
     * @param array $headers
952
     *
953
     * @return Response with json encoded data
954
     */
955
    protected function renderJson($data, $status = 200, $headers = [])
956
    {
957
        return new JsonResponse($data, $status, $headers);
958
    }
959
960
    /**
961
     * Returns true if the request is a XMLHttpRequest.
962
     *
963
     * @return bool True if the request is an XMLHttpRequest, false otherwise
964
     */
965
    protected function isXmlHttpRequest()
966
    {
967
        $request = $this->getRequest();
968
969
        return $request->isXmlHttpRequest() || $request->get('_xml_http_request');
970
    }
971
972
    /**
973
     * Returns the correct RESTful verb, given either by the request itself or
974
     * via the "_method" parameter.
975
     *
976
     * @return string HTTP method, either
977
     */
978
    protected function getRestMethod()
979
    {
980
        $request = $this->getRequest();
981
982
        if (Request::getHttpMethodParameterOverride() || !$request->request->has('_method')) {
983
            return $request->getMethod();
984
        }
985
986
        return $request->request->get('_method');
987
    }
988
989
    /**
990
     * Contextualize the admin class depends on the current request.
991
     *
992
     * @throws \RuntimeException
993
     */
994
    protected function configure()
995
    {
996
        $request = $this->getRequest();
997
998
        $adminCode = $request->get('_sonata_admin');
999
1000
        if (!$adminCode) {
1001
            throw new \RuntimeException(sprintf(
1002
                'There is no `_sonata_admin` defined for the controller `%s` and the current route `%s`',
1003
                get_class($this),
1004
                $request->get('_route')
1005
            ));
1006
        }
1007
1008
        $this->admin = $this->container->get('sonata.admin.pool')->getAdminByAdminCode($adminCode);
1009
1010
        if (!$this->admin) {
1011
            throw new \RuntimeException(sprintf(
1012
                'Unable to find the admin class related to the current controller (%s)',
1013
                get_class($this)
1014
            ));
1015
        }
1016
1017
        $rootAdmin = $this->admin;
1018
1019
        while ($rootAdmin->isChild()) {
1020
            $rootAdmin->setCurrentChild(true);
1021
            $rootAdmin = $rootAdmin->getParent();
1022
        }
1023
1024
        $rootAdmin->setRequest($request);
1025
1026
        if ($request->get('uniqid')) {
1027
            $this->admin->setUniqid($request->get('uniqid'));
1028
        }
1029
    }
1030
1031
    /**
1032
     * Proxy for the logger service of the container.
1033
     * If no such service is found, a NullLogger is returned.
1034
     *
1035
     * @return LoggerInterface
1036
     */
1037
    protected function getLogger()
1038
    {
1039
        if ($this->container->has('logger')) {
1040
            return $this->container->get('logger');
1041
        }
1042
1043
        return new NullLogger();
1044
    }
1045
1046
    /**
1047
     * Returns the base template name.
1048
     *
1049
     * @return string The template name
1050
     */
1051
    protected function getBaseTemplate()
1052
    {
1053
        if ($this->isXmlHttpRequest()) {
1054
            return $this->admin->getTemplate('ajax');
1055
        }
1056
1057
        return $this->admin->getTemplate('layout');
1058
    }
1059
1060
    /**
1061
     * @param \Exception $e
1062
     *
1063
     * @throws \Exception
1064
     */
1065
    protected function handleModelManagerException(\Exception $e)
1066
    {
1067
        if ($this->get('kernel')->isDebug()) {
1068
            throw $e;
1069
        }
1070
1071
        $context = ['exception' => $e];
1072
        if ($e->getPrevious()) {
1073
            $context['previous_exception_message'] = $e->getPrevious()->getMessage();
1074
        }
1075
        $this->getLogger()->error($e->getMessage(), $context);
1076
    }
1077
1078
    /**
1079
     * Redirect the user depend on this choice.
1080
     *
1081
     * @param object $object
1082
     *
1083
     * @return RedirectResponse
1084
     */
1085
    protected function redirectTo($object)
1086
    {
1087
        $request = $this->getRequest();
1088
1089
        $url = false;
1090
1091
        if (null !== $request->get('btn_update_and_list')) {
1092
            $url = $this->admin->generateUrl('list');
1093
        }
1094
        if (null !== $request->get('btn_create_and_list')) {
1095
            $url = $this->admin->generateUrl('list');
1096
        }
1097
1098
        if (null !== $request->get('btn_create_and_create')) {
1099
            $params = [];
1100
            if ($this->admin->hasActiveSubClass()) {
1101
                $params['subclass'] = $request->get('subclass');
1102
            }
1103
            $url = $this->admin->generateUrl('create', $params);
1104
        }
1105
1106
        if ('DELETE' === $this->getRestMethod()) {
1107
            $url = $this->admin->generateUrl('list');
1108
        }
1109
1110
        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...
1111
            foreach (['edit', 'show'] as $route) {
1112
                if ($this->admin->hasRoute($route) && $this->admin->hasAccess($route, $object)) {
1113
                    $url = $this->admin->generateObjectUrl($route, $object);
1114
1115
                    break;
1116
                }
1117
            }
1118
        }
1119
1120
        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...
1121
            $url = $this->admin->generateUrl('list');
1122
        }
1123
1124
        return new RedirectResponse($url);
1125
    }
1126
1127
    /**
1128
     * Returns true if the preview is requested to be shown.
1129
     *
1130
     * @return bool
1131
     */
1132
    protected function isPreviewRequested()
1133
    {
1134
        $request = $this->getRequest();
1135
1136
        return null !== $request->get('btn_preview');
1137
    }
1138
1139
    /**
1140
     * Returns true if the preview has been approved.
1141
     *
1142
     * @return bool
1143
     */
1144
    protected function isPreviewApproved()
1145
    {
1146
        $request = $this->getRequest();
1147
1148
        return null !== $request->get('btn_preview_approve');
1149
    }
1150
1151
    /**
1152
     * Returns true if the request is in the preview workflow.
1153
     *
1154
     * That means either a preview is requested or the preview has already been shown
1155
     * and it got approved/declined.
1156
     *
1157
     * @return bool
1158
     */
1159
    protected function isInPreviewMode()
1160
    {
1161
        return $this->admin->supportsPreviewMode()
1162
        && ($this->isPreviewRequested()
1163
            || $this->isPreviewApproved()
1164
            || $this->isPreviewDeclined());
1165
    }
1166
1167
    /**
1168
     * Returns true if the preview has been declined.
1169
     *
1170
     * @return bool
1171
     */
1172
    protected function isPreviewDeclined()
1173
    {
1174
        $request = $this->getRequest();
1175
1176
        return null !== $request->get('btn_preview_decline');
1177
    }
1178
1179
    /**
1180
     * Gets ACL users.
1181
     *
1182
     * @return \Traversable
1183
     */
1184
    protected function getAclUsers()
1185
    {
1186
        $aclUsers = [];
1187
1188
        $userManagerServiceName = $this->container->getParameter('sonata.admin.security.acl_user_manager');
1189
        if (null !== $userManagerServiceName && $this->has($userManagerServiceName)) {
1190
            $userManager = $this->get($userManagerServiceName);
1191
1192
            if (method_exists($userManager, 'findUsers')) {
1193
                $aclUsers = $userManager->findUsers();
1194
            }
1195
        }
1196
1197
        return is_array($aclUsers) ? new \ArrayIterator($aclUsers) : $aclUsers;
1198
    }
1199
1200
    /**
1201
     * Gets ACL roles.
1202
     *
1203
     * @return \Traversable
1204
     */
1205
    protected function getAclRoles()
1206
    {
1207
        $aclRoles = [];
1208
        $roleHierarchy = $this->container->getParameter('security.role_hierarchy.roles');
1209
        $pool = $this->container->get('sonata.admin.pool');
1210
1211
        foreach ($pool->getAdminServiceIds() as $id) {
1212
            try {
1213
                $admin = $pool->getInstance($id);
1214
            } catch (\Exception $e) {
1215
                continue;
1216
            }
1217
1218
            $baseRole = $admin->getSecurityHandler()->getBaseRole($admin);
1219
            foreach ($admin->getSecurityInformation() as $role => $permissions) {
1220
                $role = sprintf($baseRole, $role);
1221
                $aclRoles[] = $role;
1222
            }
1223
        }
1224
1225
        foreach ($roleHierarchy as $name => $roles) {
1226
            $aclRoles[] = $name;
1227
            $aclRoles = array_merge($aclRoles, $roles);
1228
        }
1229
1230
        $aclRoles = array_unique($aclRoles);
1231
1232
        return is_array($aclRoles) ? new \ArrayIterator($aclRoles) : $aclRoles;
1233
    }
1234
1235
    /**
1236
     * Validate CSRF token for action without form.
1237
     *
1238
     * @param string $intention
1239
     *
1240
     * @throws HttpException
1241
     */
1242
    protected function validateCsrfToken($intention)
1243
    {
1244
        $request = $this->getRequest();
1245
        $token = $request->request->get('_sonata_csrf_token', false);
1246
1247
        if ($this->container->has('security.csrf.token_manager')) { // SF3.0
1248
            $valid = $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($intention, $token));
1249
        } elseif ($this->container->has('form.csrf_provider')) { // < SF3.0
1250
            $valid = $this->container->get('form.csrf_provider')->isCsrfTokenValid($intention, $token);
1251
        } else {
1252
            return;
1253
        }
1254
1255
        if (!$valid) {
1256
            throw new HttpException(400, 'The csrf token is not valid, CSRF attack?');
1257
        }
1258
    }
1259
1260
    /**
1261
     * Escape string for html output.
1262
     *
1263
     * @param string $s
1264
     *
1265
     * @return string
1266
     */
1267
    protected function escapeHtml($s)
1268
    {
1269
        return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1270
    }
1271
1272
    /**
1273
     * Get CSRF token.
1274
     *
1275
     * @param string $intention
1276
     *
1277
     * @return string|false
1278
     */
1279
    protected function getCsrfToken($intention)
1280
    {
1281
        if ($this->container->has('security.csrf.token_manager')) {
1282
            return $this->container->get('security.csrf.token_manager')->getToken($intention)->getValue();
1283
        }
1284
1285
        // TODO: Remove it when bumping requirements to SF 2.4+
1286
        if ($this->container->has('form.csrf_provider')) {
1287
            return $this->container->get('form.csrf_provider')->generateCsrfToken($intention);
1288
        }
1289
1290
        return false;
1291
    }
1292
1293
    /**
1294
     * This method can be overloaded in your custom CRUD controller.
1295
     * It's called from createAction.
1296
     *
1297
     * @param Request $request
1298
     * @param mixed   $object
1299
     *
1300
     * @return Response|null
1301
     */
1302
    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...
1303
    {
1304
    }
1305
1306
    /**
1307
     * This method can be overloaded in your custom CRUD controller.
1308
     * It's called from editAction.
1309
     *
1310
     * @param Request $request
1311
     * @param mixed   $object
1312
     *
1313
     * @return Response|null
1314
     */
1315
    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...
1316
    {
1317
    }
1318
1319
    /**
1320
     * This method can be overloaded in your custom CRUD controller.
1321
     * It's called from deleteAction.
1322
     *
1323
     * @param Request $request
1324
     * @param mixed   $object
1325
     *
1326
     * @return Response|null
1327
     */
1328
    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...
1329
    {
1330
    }
1331
1332
    /**
1333
     * This method can be overloaded in your custom CRUD controller.
1334
     * It's called from showAction.
1335
     *
1336
     * @param Request $request
1337
     * @param mixed   $object
1338
     *
1339
     * @return Response|null
1340
     */
1341
    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...
1342
    {
1343
    }
1344
1345
    /**
1346
     * This method can be overloaded in your custom CRUD controller.
1347
     * It's called from listAction.
1348
     *
1349
     * @param Request $request
1350
     *
1351
     * @return Response|null
1352
     */
1353
    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...
1354
    {
1355
    }
1356
1357
    /**
1358
     * Translate a message id.
1359
     *
1360
     * @param string $id
1361
     * @param array  $parameters
1362
     * @param string $domain
1363
     * @param string $locale
1364
     *
1365
     * @return string translated string
1366
     */
1367
    final protected function trans($id, array $parameters = [], $domain = null, $locale = null)
1368
    {
1369
        $domain = $domain ?: $this->admin->getTranslationDomain();
1370
1371
        return $this->get('translator')->trans($id, $parameters, $domain, $locale);
1372
    }
1373
1374
    /**
1375
     * Sets the admin form theme to form view. Used for compatibility between Symfony versions.
1376
     *
1377
     * @param FormView $formView
1378
     * @param string   $theme
1379
     */
1380
    private function setFormTheme(FormView $formView, $theme)
1381
    {
1382
        $twig = $this->get('twig');
1383
1384
        // BC for Symfony < 3.2 where this runtime does not exists
1385
        if (!method_exists(AppVariable::class, 'getToken')) {
1386
            $twig->getExtension(FormExtension::class)->renderer->setTheme($formView, $theme);
1387
1388
            return;
1389
        }
1390
1391
        // BC for Symfony < 3.4 where runtime should be TwigRenderer
1392
        if (!method_exists(DebugCommand::class, 'getLoaderPaths')) {
1393
            $twig->getRuntime(TwigRenderer::class)->setTheme($formView, $theme);
1394
1395
            return;
1396
        }
1397
1398
        $twig->getRuntime(FormRenderer::class)->setTheme($formView, $theme);
1399
    }
1400
}
1401