Completed
Pull Request — master (#4788)
by Jordi Sala
08:56
created

CRUDController::render()   A

Complexity

Conditions 4
Paths 8

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 8
nop 3
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
     * {@inheritdoc}
66
     */
67
    public function render($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 parent::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->render($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->render($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->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
        $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->render($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
            $this->addFlash(
462
                'sonata_flash_info',
463
                $this->trans('flash_batch_no_elements_processed', [], 'SonataAdminBundle')
464
            );
465
466
            return new RedirectResponse(
467
                $this->admin->generateUrl('list', [
468
                    'filter' => $this->admin->getFilterParameters(),
469
                ])
470
            );
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
            );
503
        }
504
505
        $newObject = $this->admin->getNewInstance();
506
507
        $preResponse = $this->preCreate($request, $newObject);
508
        if (null !== $preResponse) {
509
            return $preResponse;
510
        }
511
512
        $this->admin->setSubject($newObject);
513
514
        /** @var $form \Symfony\Component\Form\Form */
515
        $form = $this->admin->getForm();
516
        $form->setData($newObject);
517
        $form->handleRequest($request);
518
519
        if ($form->isSubmitted()) {
520
            $this->admin->preValidate($newObject);
521
            $isFormValid = $form->isValid();
522
523
            // persist if the form was valid and if in preview mode the preview was approved
524
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
525
                $submittedObject = $form->getData();
526
                $this->admin->setSubject($submittedObject);
527
                $this->admin->checkAccess('create', $submittedObject);
528
529
                try {
530
                    $newObject = $this->admin->create($submittedObject);
531
532
                    if ($this->isXmlHttpRequest()) {
533
                        return $this->renderJson([
534
                            'result' => 'ok',
535
                            'objectId' => $this->admin->getNormalizedIdentifier($newObject),
536
                        ], 200, []);
537
                    }
538
539
                    $this->addFlash(
540
                        'sonata_flash_success',
541
                        $this->trans(
542
                            'flash_create_success',
543
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
544
                            'SonataAdminBundle'
545
                        )
546
                    );
547
548
                    // redirect to edit mode
549
                    return $this->redirectTo($newObject);
550
                } catch (ModelManagerException $e) {
551
                    $this->handleModelManagerException($e);
552
553
                    $isFormValid = false;
554
                }
555
            }
556
557
            // show an error message if the form failed validation
558
            if (!$isFormValid) {
559
                if (!$this->isXmlHttpRequest()) {
560
                    $this->addFlash(
561
                        'sonata_flash_error',
562
                        $this->trans(
563
                            'flash_create_error',
564
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
565
                            'SonataAdminBundle'
566
                        )
567
                    );
568
                }
569
            } elseif ($this->isPreviewRequested()) {
570
                // pick the preview template if the form was valid and preview was requested
571
                $templateKey = 'preview';
572
                $this->admin->getShow();
573
            }
574
        }
575
576
        $formView = $form->createView();
577
        // set the theme for the current Admin Form
578
        $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...
579
580
        return $this->render($this->admin->getTemplate($templateKey), [
581
            'action' => 'create',
582
            'form' => $formView,
583
            'object' => $newObject,
584
            'objectId' => null,
585
        ], null);
586
    }
587
588
    /**
589
     * Show action.
590
     *
591
     * @param int|string|null $id
592
     *
593
     * @return Response
594
     *
595
     * @throws NotFoundHttpException If the object does not exist
596
     * @throws AccessDeniedException If access is not granted
597
     */
598
    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...
599
    {
600
        $request = $this->getRequest();
601
        $id = $request->get($this->admin->getIdParameter());
602
603
        $object = $this->admin->getObject($id);
604
605
        if (!$object) {
606
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
607
        }
608
609
        $this->admin->checkAccess('show', $object);
610
611
        $preResponse = $this->preShow($request, $object);
612
        if (null !== $preResponse) {
613
            return $preResponse;
614
        }
615
616
        $this->admin->setSubject($object);
617
618
        return $this->render($this->admin->getTemplate('show'), [
619
            'action' => 'show',
620
            'object' => $object,
621
            'elements' => $this->admin->getShow(),
622
        ], null);
623
    }
624
625
    /**
626
     * Show history revisions for object.
627
     *
628
     * @param int|string|null $id
629
     *
630
     * @return Response
631
     *
632
     * @throws AccessDeniedException If access is not granted
633
     * @throws NotFoundHttpException If the object does not exist or the audit reader is not available
634
     */
635
    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...
636
    {
637
        $request = $this->getRequest();
638
        $id = $request->get($this->admin->getIdParameter());
639
640
        $object = $this->admin->getObject($id);
641
642
        if (!$object) {
643
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
644
        }
645
646
        $this->admin->checkAccess('history', $object);
647
648
        $manager = $this->get('sonata.admin.audit.manager');
649
650
        if (!$manager->hasReader($this->admin->getClass())) {
651
            throw $this->createNotFoundException(
652
                sprintf(
653
                    'unable to find the audit reader for class : %s',
654
                    $this->admin->getClass()
655
                )
656
            );
657
        }
658
659
        $reader = $manager->getReader($this->admin->getClass());
660
661
        $revisions = $reader->findRevisions($this->admin->getClass(), $id);
662
663
        return $this->render($this->admin->getTemplate('history'), [
664
            'action' => 'history',
665
            'object' => $object,
666
            'revisions' => $revisions,
667
            'currentRevision' => $revisions ? current($revisions) : false,
668
        ], null);
669
    }
670
671
    /**
672
     * View history revision of object.
673
     *
674
     * @param int|string|null $id
675
     * @param string|null     $revision
676
     *
677
     * @return Response
678
     *
679
     * @throws AccessDeniedException If access is not granted
680
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
681
     */
682
    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...
683
    {
684
        $request = $this->getRequest();
685
        $id = $request->get($this->admin->getIdParameter());
686
687
        $object = $this->admin->getObject($id);
688
689
        if (!$object) {
690
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
691
        }
692
693
        $this->admin->checkAccess('historyViewRevision', $object);
694
695
        $manager = $this->get('sonata.admin.audit.manager');
696
697
        if (!$manager->hasReader($this->admin->getClass())) {
698
            throw $this->createNotFoundException(
699
                sprintf(
700
                    'unable to find the audit reader for class : %s',
701
                    $this->admin->getClass()
702
                )
703
            );
704
        }
705
706
        $reader = $manager->getReader($this->admin->getClass());
707
708
        // retrieve the revisioned object
709
        $object = $reader->find($this->admin->getClass(), $id, $revision);
710
711
        if (!$object) {
712
            throw $this->createNotFoundException(
713
                sprintf(
714
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
715
                    $id,
716
                    $revision,
717
                    $this->admin->getClass()
718
                )
719
            );
720
        }
721
722
        $this->admin->setSubject($object);
723
724
        return $this->render($this->admin->getTemplate('show'), [
725
            'action' => 'show',
726
            'object' => $object,
727
            'elements' => $this->admin->getShow(),
728
        ], null);
729
    }
730
731
    /**
732
     * Compare history revisions of object.
733
     *
734
     * @param int|string|null $id
735
     * @param int|string|null $base_revision
736
     * @param int|string|null $compare_revision
737
     *
738
     * @return Response
739
     *
740
     * @throws AccessDeniedException If access is not granted
741
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
742
     */
743
    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...
744
    {
745
        $request = $this->getRequest();
746
747
        $this->admin->checkAccess('historyCompareRevisions');
748
749
        $id = $request->get($this->admin->getIdParameter());
750
751
        $object = $this->admin->getObject($id);
752
753
        if (!$object) {
754
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
755
        }
756
757
        $manager = $this->get('sonata.admin.audit.manager');
758
759
        if (!$manager->hasReader($this->admin->getClass())) {
760
            throw $this->createNotFoundException(
761
                sprintf(
762
                    'unable to find the audit reader for class : %s',
763
                    $this->admin->getClass()
764
                )
765
            );
766
        }
767
768
        $reader = $manager->getReader($this->admin->getClass());
769
770
        // retrieve the base revision
771
        $base_object = $reader->find($this->admin->getClass(), $id, $base_revision);
772
        if (!$base_object) {
773
            throw $this->createNotFoundException(
774
                sprintf(
775
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
776
                    $id,
777
                    $base_revision,
778
                    $this->admin->getClass()
779
                )
780
            );
781
        }
782
783
        // retrieve the compare revision
784
        $compare_object = $reader->find($this->admin->getClass(), $id, $compare_revision);
785
        if (!$compare_object) {
786
            throw $this->createNotFoundException(
787
                sprintf(
788
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
789
                    $id,
790
                    $compare_revision,
791
                    $this->admin->getClass()
792
                )
793
            );
794
        }
795
796
        $this->admin->setSubject($base_object);
797
798
        return $this->render($this->admin->getTemplate('show_compare'), [
799
            'action' => 'show',
800
            'object' => $base_object,
801
            'object_compare' => $compare_object,
802
            'elements' => $this->admin->getShow(),
803
        ], null);
804
    }
805
806
    /**
807
     * Export data to specified format.
808
     *
809
     * @param Request $request
810
     *
811
     * @return Response
812
     *
813
     * @throws AccessDeniedException If access is not granted
814
     * @throws \RuntimeException     If the export format is invalid
815
     */
816
    public function exportAction(Request $request)
817
    {
818
        $this->admin->checkAccess('export');
819
820
        $format = $request->get('format');
821
822
        // NEXT_MAJOR: remove the check
823
        if (!$this->has('sonata.admin.admin_exporter')) {
824
            @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...
825
                'Not registering the exporter bundle is deprecated since version 3.14.'
826
                .' You must register it to be able to use the export action in 4.0.',
827
                E_USER_DEPRECATED
828
            );
829
            $allowedExportFormats = (array) $this->admin->getExportFormats();
830
831
            $class = $this->admin->getClass();
832
            $filename = sprintf(
833
                'export_%s_%s.%s',
834
                strtolower(substr($class, strripos($class, '\\') + 1)),
835
                date('Y_m_d_H_i_s', strtotime('now')),
836
                $format
837
            );
838
            $exporter = $this->get('sonata.admin.exporter');
839
        } else {
840
            $adminExporter = $this->get('sonata.admin.admin_exporter');
841
            $allowedExportFormats = $adminExporter->getAvailableFormats($this->admin);
842
            $filename = $adminExporter->getExportFilename($this->admin, $format);
843
            $exporter = $this->get('sonata.exporter.exporter');
844
        }
845
846
        if (!in_array($format, $allowedExportFormats)) {
847
            throw new \RuntimeException(
848
                sprintf(
849
                    'Export in format `%s` is not allowed for class: `%s`. Allowed formats are: `%s`',
850
                    $format,
851
                    $this->admin->getClass(),
852
                    implode(', ', $allowedExportFormats)
853
                )
854
            );
855
        }
856
857
        return $exporter->getResponse(
858
            $format,
859
            $filename,
860
            $this->admin->getDataSourceIterator()
861
        );
862
    }
863
864
    /**
865
     * Returns the Response object associated to the acl action.
866
     *
867
     * @param int|string|null $id
868
     *
869
     * @return Response|RedirectResponse
870
     *
871
     * @throws AccessDeniedException If access is not granted
872
     * @throws NotFoundHttpException If the object does not exist or the ACL is not enabled
873
     */
874
    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...
875
    {
876
        $request = $this->getRequest();
877
878
        if (!$this->admin->isAclEnabled()) {
879
            throw $this->createNotFoundException('ACL are not enabled for this admin');
880
        }
881
882
        $id = $request->get($this->admin->getIdParameter());
883
884
        $object = $this->admin->getObject($id);
885
886
        if (!$object) {
887
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
888
        }
889
890
        $this->admin->checkAccess('acl', $object);
891
892
        $this->admin->setSubject($object);
893
        $aclUsers = $this->getAclUsers();
894
        $aclRoles = $this->getAclRoles();
895
896
        $adminObjectAclManipulator = $this->get('sonata.admin.object.manipulator.acl.admin');
897
        $adminObjectAclData = new AdminObjectAclData(
898
            $this->admin,
899
            $object,
900
            $aclUsers,
901
            $adminObjectAclManipulator->getMaskBuilderClass(),
902
            $aclRoles
903
        );
904
905
        $aclUsersForm = $adminObjectAclManipulator->createAclUsersForm($adminObjectAclData);
906
        $aclRolesForm = $adminObjectAclManipulator->createAclRolesForm($adminObjectAclData);
907
908
        if ('POST' === $request->getMethod()) {
909
            if ($request->request->has(AdminObjectAclManipulator::ACL_USERS_FORM_NAME)) {
910
                $form = $aclUsersForm;
911
                $updateMethod = 'updateAclUsers';
912
            } elseif ($request->request->has(AdminObjectAclManipulator::ACL_ROLES_FORM_NAME)) {
913
                $form = $aclRolesForm;
914
                $updateMethod = 'updateAclRoles';
915
            }
916
917
            if (isset($form)) {
918
                $form->handleRequest($request);
919
920
                if ($form->isValid()) {
921
                    $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...
922
                    $this->addFlash(
923
                        'sonata_flash_success',
924
                        $this->trans('flash_acl_edit_success', [], 'SonataAdminBundle')
925
                    );
926
927
                    return new RedirectResponse($this->admin->generateObjectUrl('acl', $object));
928
                }
929
            }
930
        }
931
932
        return $this->render($this->admin->getTemplate('acl'), [
933
            'action' => 'acl',
934
            'permissions' => $adminObjectAclData->getUserPermissions(),
935
            'object' => $object,
936
            'users' => $aclUsers,
937
            'roles' => $aclRoles,
938
            'aclUsersForm' => $aclUsersForm->createView(),
939
            'aclRolesForm' => $aclRolesForm->createView(),
940
        ], null);
941
    }
942
943
    /**
944
     * @return Request
945
     */
946
    public function getRequest()
947
    {
948
        if ($this->container->has('request_stack')) {
949
            return $this->container->get('request_stack')->getCurrentRequest();
950
        }
951
952
        return $this->container->get('request');
953
    }
954
955
    /**
956
     * Render JSON.
957
     *
958
     * @param mixed $data
959
     * @param int   $status
960
     * @param array $headers
961
     *
962
     * @return Response with json encoded data
963
     */
964
    protected function renderJson($data, $status = 200, $headers = [])
965
    {
966
        return new JsonResponse($data, $status, $headers);
967
    }
968
969
    /**
970
     * Returns true if the request is a XMLHttpRequest.
971
     *
972
     * @return bool True if the request is an XMLHttpRequest, false otherwise
973
     */
974
    protected function isXmlHttpRequest()
975
    {
976
        $request = $this->getRequest();
977
978
        return $request->isXmlHttpRequest() || $request->get('_xml_http_request');
979
    }
980
981
    /**
982
     * Returns the correct RESTful verb, given either by the request itself or
983
     * via the "_method" parameter.
984
     *
985
     * @return string HTTP method, either
986
     */
987
    protected function getRestMethod()
988
    {
989
        $request = $this->getRequest();
990
991
        if (Request::getHttpMethodParameterOverride() || !$request->request->has('_method')) {
992
            return $request->getMethod();
993
        }
994
995
        return $request->request->get('_method');
996
    }
997
998
    /**
999
     * Contextualize the admin class depends on the current request.
1000
     *
1001
     * @throws \RuntimeException
1002
     */
1003
    protected function configure()
1004
    {
1005
        $request = $this->getRequest();
1006
1007
        $adminCode = $request->get('_sonata_admin');
1008
1009
        if (!$adminCode) {
1010
            throw new \RuntimeException(sprintf(
1011
                'There is no `_sonata_admin` defined for the controller `%s` and the current route `%s`',
1012
                get_class($this),
1013
                $request->get('_route')
1014
            ));
1015
        }
1016
1017
        $this->admin = $this->container->get('sonata.admin.pool')->getAdminByAdminCode($adminCode);
1018
1019
        if (!$this->admin) {
1020
            throw new \RuntimeException(sprintf(
1021
                'Unable to find the admin class related to the current controller (%s)',
1022
                get_class($this)
1023
            ));
1024
        }
1025
1026
        $rootAdmin = $this->admin;
1027
1028
        while ($rootAdmin->isChild()) {
1029
            $rootAdmin->setCurrentChild(true);
1030
            $rootAdmin = $rootAdmin->getParent();
1031
        }
1032
1033
        $rootAdmin->setRequest($request);
1034
1035
        if ($request->get('uniqid')) {
1036
            $this->admin->setUniqid($request->get('uniqid'));
1037
        }
1038
    }
1039
1040
    /**
1041
     * Proxy for the logger service of the container.
1042
     * If no such service is found, a NullLogger is returned.
1043
     *
1044
     * @return LoggerInterface
1045
     */
1046
    protected function getLogger()
1047
    {
1048
        if ($this->container->has('logger')) {
1049
            return $this->container->get('logger');
1050
        }
1051
1052
        return new NullLogger();
1053
    }
1054
1055
    /**
1056
     * Returns the base template name.
1057
     *
1058
     * @return string The template name
1059
     */
1060
    protected function getBaseTemplate()
1061
    {
1062
        if ($this->isXmlHttpRequest()) {
1063
            return $this->admin->getTemplate('ajax');
1064
        }
1065
1066
        return $this->admin->getTemplate('layout');
1067
    }
1068
1069
    /**
1070
     * @param \Exception $e
1071
     *
1072
     * @throws \Exception
1073
     */
1074
    protected function handleModelManagerException(\Exception $e)
1075
    {
1076
        if ($this->get('kernel')->isDebug()) {
1077
            throw $e;
1078
        }
1079
1080
        $context = ['exception' => $e];
1081
        if ($e->getPrevious()) {
1082
            $context['previous_exception_message'] = $e->getPrevious()->getMessage();
1083
        }
1084
        $this->getLogger()->error($e->getMessage(), $context);
1085
    }
1086
1087
    /**
1088
     * Redirect the user depend on this choice.
1089
     *
1090
     * @param object $object
1091
     *
1092
     * @return RedirectResponse
1093
     */
1094
    protected function redirectTo($object)
1095
    {
1096
        $request = $this->getRequest();
1097
1098
        $url = false;
1099
1100
        if (null !== $request->get('btn_update_and_list')) {
1101
            $url = $this->admin->generateUrl('list');
1102
        }
1103
        if (null !== $request->get('btn_create_and_list')) {
1104
            $url = $this->admin->generateUrl('list');
1105
        }
1106
1107
        if (null !== $request->get('btn_create_and_create')) {
1108
            $params = [];
1109
            if ($this->admin->hasActiveSubClass()) {
1110
                $params['subclass'] = $request->get('subclass');
1111
            }
1112
            $url = $this->admin->generateUrl('create', $params);
1113
        }
1114
1115
        if ('DELETE' === $this->getRestMethod()) {
1116
            $url = $this->admin->generateUrl('list');
1117
        }
1118
1119
        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...
1120
            foreach (['edit', 'show'] as $route) {
1121
                if ($this->admin->hasRoute($route) && $this->admin->hasAccess($route, $object)) {
1122
                    $url = $this->admin->generateObjectUrl($route, $object);
1123
1124
                    break;
1125
                }
1126
            }
1127
        }
1128
1129
        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...
1130
            $url = $this->admin->generateUrl('list');
1131
        }
1132
1133
        return new RedirectResponse($url);
1134
    }
1135
1136
    /**
1137
     * Returns true if the preview is requested to be shown.
1138
     *
1139
     * @return bool
1140
     */
1141
    protected function isPreviewRequested()
1142
    {
1143
        $request = $this->getRequest();
1144
1145
        return null !== $request->get('btn_preview');
1146
    }
1147
1148
    /**
1149
     * Returns true if the preview has been approved.
1150
     *
1151
     * @return bool
1152
     */
1153
    protected function isPreviewApproved()
1154
    {
1155
        $request = $this->getRequest();
1156
1157
        return null !== $request->get('btn_preview_approve');
1158
    }
1159
1160
    /**
1161
     * Returns true if the request is in the preview workflow.
1162
     *
1163
     * That means either a preview is requested or the preview has already been shown
1164
     * and it got approved/declined.
1165
     *
1166
     * @return bool
1167
     */
1168
    protected function isInPreviewMode()
1169
    {
1170
        return $this->admin->supportsPreviewMode()
1171
        && ($this->isPreviewRequested()
1172
            || $this->isPreviewApproved()
1173
            || $this->isPreviewDeclined());
1174
    }
1175
1176
    /**
1177
     * Returns true if the preview has been declined.
1178
     *
1179
     * @return bool
1180
     */
1181
    protected function isPreviewDeclined()
1182
    {
1183
        $request = $this->getRequest();
1184
1185
        return null !== $request->get('btn_preview_decline');
1186
    }
1187
1188
    /**
1189
     * Gets ACL users.
1190
     *
1191
     * @return \Traversable
1192
     */
1193
    protected function getAclUsers()
1194
    {
1195
        $aclUsers = [];
1196
1197
        $userManagerServiceName = $this->container->getParameter('sonata.admin.security.acl_user_manager');
1198
        if (null !== $userManagerServiceName && $this->has($userManagerServiceName)) {
1199
            $userManager = $this->get($userManagerServiceName);
1200
1201
            if (method_exists($userManager, 'findUsers')) {
1202
                $aclUsers = $userManager->findUsers();
1203
            }
1204
        }
1205
1206
        return is_array($aclUsers) ? new \ArrayIterator($aclUsers) : $aclUsers;
1207
    }
1208
1209
    /**
1210
     * Gets ACL roles.
1211
     *
1212
     * @return \Traversable
1213
     */
1214
    protected function getAclRoles()
1215
    {
1216
        $aclRoles = [];
1217
        $roleHierarchy = $this->container->getParameter('security.role_hierarchy.roles');
1218
        $pool = $this->container->get('sonata.admin.pool');
1219
1220
        foreach ($pool->getAdminServiceIds() as $id) {
1221
            try {
1222
                $admin = $pool->getInstance($id);
1223
            } catch (\Exception $e) {
1224
                continue;
1225
            }
1226
1227
            $baseRole = $admin->getSecurityHandler()->getBaseRole($admin);
1228
            foreach ($admin->getSecurityInformation() as $role => $permissions) {
1229
                $role = sprintf($baseRole, $role);
1230
                $aclRoles[] = $role;
1231
            }
1232
        }
1233
1234
        foreach ($roleHierarchy as $name => $roles) {
1235
            $aclRoles[] = $name;
1236
            $aclRoles = array_merge($aclRoles, $roles);
1237
        }
1238
1239
        $aclRoles = array_unique($aclRoles);
1240
1241
        return is_array($aclRoles) ? new \ArrayIterator($aclRoles) : $aclRoles;
1242
    }
1243
1244
    /**
1245
     * Validate CSRF token for action without form.
1246
     *
1247
     * @param string $intention
1248
     *
1249
     * @throws HttpException
1250
     */
1251
    protected function validateCsrfToken($intention)
1252
    {
1253
        $request = $this->getRequest();
1254
        $token = $request->request->get('_sonata_csrf_token', false);
1255
1256
        if ($this->container->has('security.csrf.token_manager')) { // SF3.0
1257
            $valid = $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($intention, $token));
1258
        } elseif ($this->container->has('form.csrf_provider')) { // < SF3.0
1259
            $valid = $this->container->get('form.csrf_provider')->isCsrfTokenValid($intention, $token);
1260
        } else {
1261
            return;
1262
        }
1263
1264
        if (!$valid) {
1265
            throw new HttpException(400, 'The csrf token is not valid, CSRF attack?');
1266
        }
1267
    }
1268
1269
    /**
1270
     * Escape string for html output.
1271
     *
1272
     * @param string $s
1273
     *
1274
     * @return string
1275
     */
1276
    protected function escapeHtml($s)
1277
    {
1278
        return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1279
    }
1280
1281
    /**
1282
     * Get CSRF token.
1283
     *
1284
     * @param string $intention
1285
     *
1286
     * @return string|false
1287
     */
1288
    protected function getCsrfToken($intention)
1289
    {
1290
        if ($this->container->has('security.csrf.token_manager')) {
1291
            return $this->container->get('security.csrf.token_manager')->getToken($intention)->getValue();
1292
        }
1293
1294
        // TODO: Remove it when bumping requirements to SF 2.4+
1295
        if ($this->container->has('form.csrf_provider')) {
1296
            return $this->container->get('form.csrf_provider')->generateCsrfToken($intention);
1297
        }
1298
1299
        return false;
1300
    }
1301
1302
    /**
1303
     * This method can be overloaded in your custom CRUD controller.
1304
     * It's called from createAction.
1305
     *
1306
     * @param Request $request
1307
     * @param mixed   $object
1308
     *
1309
     * @return Response|null
1310
     */
1311
    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...
1312
    {
1313
    }
1314
1315
    /**
1316
     * This method can be overloaded in your custom CRUD controller.
1317
     * It's called from editAction.
1318
     *
1319
     * @param Request $request
1320
     * @param mixed   $object
1321
     *
1322
     * @return Response|null
1323
     */
1324
    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...
1325
    {
1326
    }
1327
1328
    /**
1329
     * This method can be overloaded in your custom CRUD controller.
1330
     * It's called from deleteAction.
1331
     *
1332
     * @param Request $request
1333
     * @param mixed   $object
1334
     *
1335
     * @return Response|null
1336
     */
1337
    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...
1338
    {
1339
    }
1340
1341
    /**
1342
     * This method can be overloaded in your custom CRUD controller.
1343
     * It's called from showAction.
1344
     *
1345
     * @param Request $request
1346
     * @param mixed   $object
1347
     *
1348
     * @return Response|null
1349
     */
1350
    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...
1351
    {
1352
    }
1353
1354
    /**
1355
     * This method can be overloaded in your custom CRUD controller.
1356
     * It's called from listAction.
1357
     *
1358
     * @param Request $request
1359
     *
1360
     * @return Response|null
1361
     */
1362
    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...
1363
    {
1364
    }
1365
1366
    /**
1367
     * Translate a message id.
1368
     *
1369
     * @param string $id
1370
     * @param array  $parameters
1371
     * @param string $domain
1372
     * @param string $locale
1373
     *
1374
     * @return string translated string
1375
     */
1376
    final protected function trans($id, array $parameters = [], $domain = null, $locale = null)
1377
    {
1378
        $domain = $domain ?: $this->admin->getTranslationDomain();
1379
1380
        return $this->get('translator')->trans($id, $parameters, $domain, $locale);
1381
    }
1382
1383
    /**
1384
     * Sets the admin form theme to form view. Used for compatibility between Symfony versions.
1385
     *
1386
     * @param FormView $formView
1387
     * @param string   $theme
1388
     */
1389
    private function setFormTheme(FormView $formView, $theme)
1390
    {
1391
        $twig = $this->get('twig');
1392
1393
        // BC for Symfony < 3.2 where this runtime does not exists
1394
        if (!method_exists(AppVariable::class, 'getToken')) {
1395
            $twig->getExtension(FormExtension::class)->renderer->setTheme($formView, $theme);
1396
1397
            return;
1398
        }
1399
1400
        // BC for Symfony < 3.4 where runtime should be TwigRenderer
1401
        if (!method_exists(DebugCommand::class, 'getLoaderPaths')) {
1402
            $twig->getRuntime(TwigRenderer::class)->setTheme($formView, $theme);
1403
1404
            return;
1405
        }
1406
1407
        $twig->getRuntime(FormRenderer::class)->setTheme($formView, $theme);
1408
    }
1409
}
1410