Completed
Push — 3.x ( 1f2489...bdde6a )
by Grégoire
03:40
created

CRUDController::historyAction()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 35
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 21
nc 3
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
     * {@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
            //TODO: remove this check for 4.0
274
            if (method_exists($this->admin, 'preValidate')) {
275
                $this->admin->preValidate($existingObject);
0 ignored issues
show
Bug introduced by
The method preValidate() does not exist on Sonata\AdminBundle\Admin\AdminInterface. Did you maybe mean validate()?

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

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

Loading history...
276
            }
277
            $isFormValid = $form->isValid();
278
279
            // persist if the form was valid and if in preview mode the preview was approved
280
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
281
                $submittedObject = $form->getData();
282
                $this->admin->setSubject($submittedObject);
283
284
                try {
285
                    $existingObject = $this->admin->update($submittedObject);
286
287
                    if ($this->isXmlHttpRequest()) {
288
                        return $this->renderJson([
289
                            'result' => 'ok',
290
                            'objectId' => $objectId,
291
                            'objectName' => $this->escapeHtml($this->admin->toString($existingObject)),
292
                        ], 200, []);
293
                    }
294
295
                    $this->addFlash(
296
                        'sonata_flash_success',
297
                        $this->trans(
298
                            'flash_edit_success',
299
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
300
                            'SonataAdminBundle'
301
                        )
302
                    );
303
304
                    // redirect to edit mode
305
                    return $this->redirectTo($existingObject);
306
                } catch (ModelManagerException $e) {
307
                    $this->handleModelManagerException($e);
308
309
                    $isFormValid = false;
310
                } catch (LockException $e) {
311
                    $this->addFlash('sonata_flash_error', $this->trans('flash_lock_error', [
312
                        '%name%' => $this->escapeHtml($this->admin->toString($existingObject)),
313
                        '%link_start%' => '<a href="'.$this->admin->generateObjectUrl('edit', $existingObject).'">',
314
                        '%link_end%' => '</a>',
315
                    ], 'SonataAdminBundle'));
316
                }
317
            }
318
319
            // show an error message if the form failed validation
320
            if (!$isFormValid) {
321
                if (!$this->isXmlHttpRequest()) {
322
                    $this->addFlash(
323
                        'sonata_flash_error',
324
                        $this->trans(
325
                            'flash_edit_error',
326
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
327
                            'SonataAdminBundle'
328
                        )
329
                    );
330
                }
331
            } elseif ($this->isPreviewRequested()) {
332
                // enable the preview template if the form was valid and preview was requested
333
                $templateKey = 'preview';
334
                $this->admin->getShow();
335
            }
336
        }
337
338
        $formView = $form->createView();
339
        // set the theme for the current Admin Form
340
        $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...
341
342
        return $this->render($this->admin->getTemplate($templateKey), [
343
            'action' => 'edit',
344
            'form' => $formView,
345
            'object' => $existingObject,
346
            'objectId' => $objectId,
347
        ], null);
348
    }
349
350
    /**
351
     * Batch action.
352
     *
353
     * @return Response|RedirectResponse
354
     *
355
     * @throws NotFoundHttpException If the HTTP method is not POST
356
     * @throws \RuntimeException     If the batch action is not defined
357
     */
358
    public function batchAction()
359
    {
360
        $request = $this->getRequest();
361
        $restMethod = $this->getRestMethod();
362
363
        if ('POST' !== $restMethod) {
364
            throw $this->createNotFoundException(sprintf('Invalid request type "%s", POST expected', $restMethod));
365
        }
366
367
        // check the csrf token
368
        $this->validateCsrfToken('sonata.batch');
369
370
        $confirmation = $request->get('confirmation', false);
371
372
        if ($data = json_decode($request->get('data'), true)) {
373
            $action = $data['action'];
374
            $idx = $data['idx'];
375
            $allElements = $data['all_elements'];
376
            $request->request->replace(array_merge($request->request->all(), $data));
377
        } else {
378
            $request->request->set('idx', $request->get('idx', []));
379
            $request->request->set('all_elements', $request->get('all_elements', false));
380
381
            $action = $request->get('action');
382
            $idx = $request->get('idx');
383
            $allElements = $request->get('all_elements');
384
            $data = $request->request->all();
385
386
            unset($data['_sonata_csrf_token']);
387
        }
388
389
        // NEXT_MAJOR: Remove reflection check.
390
        $reflector = new \ReflectionMethod($this->admin, 'getBatchActions');
391
        if ($reflector->getDeclaringClass()->getName() === get_class($this->admin)) {
0 ignored issues
show
introduced by
Consider using $reflector->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
392
            @trigger_error('Override Sonata\AdminBundle\Admin\AbstractAdmin::getBatchActions method'
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
393
                .' is deprecated since version 3.2.'
394
                .' Use Sonata\AdminBundle\Admin\AbstractAdmin::configureBatchActions instead.'
395
                .' The method will be final in 4.0.', E_USER_DEPRECATED
396
            );
397
        }
398
        $batchActions = $this->admin->getBatchActions();
399
        if (!array_key_exists($action, $batchActions)) {
400
            throw new \RuntimeException(sprintf('The `%s` batch action is not defined', $action));
401
        }
402
403
        $camelizedAction = Inflector::classify($action);
404
        $isRelevantAction = sprintf('batchAction%sIsRelevant', $camelizedAction);
405
406
        if (method_exists($this, $isRelevantAction)) {
407
            $nonRelevantMessage = call_user_func([$this, $isRelevantAction], $idx, $allElements, $request);
408
        } else {
409
            $nonRelevantMessage = 0 != count($idx) || $allElements; // at least one item is selected
410
        }
411
412
        if (!$nonRelevantMessage) { // default non relevant message (if false of null)
413
            $nonRelevantMessage = 'flash_batch_empty';
414
        }
415
416
        $datagrid = $this->admin->getDatagrid();
417
        $datagrid->buildPager();
418
419
        if (true !== $nonRelevantMessage) {
420
            $this->addFlash(
421
                'sonata_flash_info',
422
                $this->trans($nonRelevantMessage, [], 'SonataAdminBundle')
423
            );
424
425
            return new RedirectResponse(
426
                $this->admin->generateUrl(
427
                    'list',
428
                    ['filter' => $this->admin->getFilterParameters()]
429
                )
430
            );
431
        }
432
433
        $askConfirmation = isset($batchActions[$action]['ask_confirmation']) ?
434
            $batchActions[$action]['ask_confirmation'] :
435
            true;
436
437
        if ($askConfirmation && 'ok' != $confirmation) {
438
            $actionLabel = $batchActions[$action]['label'];
439
            $batchTranslationDomain = isset($batchActions[$action]['translation_domain']) ?
440
                $batchActions[$action]['translation_domain'] :
441
                $this->admin->getTranslationDomain();
442
443
            $formView = $datagrid->getForm()->createView();
444
            $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...
445
446
            return $this->render($this->admin->getTemplate('batch_confirmation'), [
447
                'action' => 'list',
448
                'action_label' => $actionLabel,
449
                'batch_translation_domain' => $batchTranslationDomain,
450
                'datagrid' => $datagrid,
451
                'form' => $formView,
452
                'data' => $data,
453
                'csrf_token' => $this->getCsrfToken('sonata.batch'),
454
            ], null);
455
        }
456
457
        // execute the action, batchActionXxxxx
458
        $finalAction = sprintf('batchAction%s', $camelizedAction);
459
        if (!is_callable([$this, $finalAction])) {
460
            throw new \RuntimeException(sprintf('A `%s::%s` method must be callable', get_class($this), $finalAction));
461
        }
462
463
        $query = $datagrid->getQuery();
464
465
        $query->setFirstResult(null);
466
        $query->setMaxResults(null);
467
468
        $this->admin->preBatchAction($action, $query, $idx, $allElements);
469
470
        if (count($idx) > 0) {
471
            $this->admin->getModelManager()->addIdentifiersToQuery($this->admin->getClass(), $query, $idx);
472
        } elseif (!$allElements) {
473
            $this->addFlash(
474
                'sonata_flash_info',
475
                $this->trans('flash_batch_no_elements_processed', [], 'SonataAdminBundle')
476
            );
477
478
            return new RedirectResponse(
479
                $this->admin->generateUrl('list', [
480
                    'filter' => $this->admin->getFilterParameters(),
481
                ])
482
            );
483
        }
484
485
        return call_user_func([$this, $finalAction], $query, $request);
486
    }
487
488
    /**
489
     * Create action.
490
     *
491
     * @return Response
492
     *
493
     * @throws AccessDeniedException If access is not granted
494
     */
495
    public function createAction()
496
    {
497
        $request = $this->getRequest();
498
        // the key used to lookup the template
499
        $templateKey = 'edit';
500
501
        $this->admin->checkAccess('create');
502
503
        $class = new \ReflectionClass($this->admin->hasActiveSubClass() ? $this->admin->getActiveSubClass() : $this->admin->getClass());
504
505
        if ($class->isAbstract()) {
506
            return $this->render(
507
                'SonataAdminBundle:CRUD:select_subclass.html.twig',
508
                [
509
                    'base_template' => $this->getBaseTemplate(),
510
                    'admin' => $this->admin,
511
                    'action' => 'create',
512
                ],
513
                null
514
            );
515
        }
516
517
        $newObject = $this->admin->getNewInstance();
518
519
        $preResponse = $this->preCreate($request, $newObject);
520
        if (null !== $preResponse) {
521
            return $preResponse;
522
        }
523
524
        $this->admin->setSubject($newObject);
525
526
        /** @var $form \Symfony\Component\Form\Form */
527
        $form = $this->admin->getForm();
528
        $form->setData($newObject);
529
        $form->handleRequest($request);
530
531
        if ($form->isSubmitted()) {
532
            //TODO: remove this check for 4.0
533
            if (method_exists($this->admin, 'preValidate')) {
534
                $this->admin->preValidate($newObject);
0 ignored issues
show
Bug introduced by
The method preValidate() does not exist on Sonata\AdminBundle\Admin\AdminInterface. Did you maybe mean validate()?

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

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

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