Completed
Push — 3.x ( f0e60f...df510a )
by Jordi Sala
03:29
created

CRUDController::isInPreviewMode()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

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

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

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