Completed
Push — 3.x ( 21f634...ad52e7 )
by Grégoire
03:43
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\Bundle\FrameworkBundle\Controller\Controller;
24
use Symfony\Component\DependencyInjection\ContainerInterface;
25
use Symfony\Component\Form\FormView;
26
use Symfony\Component\HttpFoundation\JsonResponse;
27
use Symfony\Component\HttpFoundation\RedirectResponse;
28
use Symfony\Component\HttpFoundation\Request;
29
use Symfony\Component\HttpFoundation\Response;
30
use Symfony\Component\HttpKernel\Exception\HttpException;
31
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
32
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
33
use Symfony\Component\Security\Csrf\CsrfToken;
34
35
/**
36
 * @author Thomas Rabaix <[email protected]>
37
 */
38
class CRUDController extends Controller
39
{
40
    /**
41
     * The related Admin class.
42
     *
43
     * @var AdminInterface
44
     */
45
    protected $admin;
46
47
    /**
48
     * Sets the Container associated with this Controller.
49
     *
50
     * @param ContainerInterface $container A ContainerInterface instance
51
     */
52
    public function setContainer(ContainerInterface $container = null)
53
    {
54
        $this->container = $container;
55
56
        $this->configure();
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function render($view, array $parameters = [], Response $response = null)
63
    {
64
        if (!$this->isXmlHttpRequest()) {
65
            $parameters['breadcrumbs_builder'] = $this->get('sonata.admin.breadcrumbs_builder');
66
        }
67
        $parameters['admin'] = isset($parameters['admin']) ?
68
            $parameters['admin'] :
69
            $this->admin;
70
71
        $parameters['base_template'] = isset($parameters['base_template']) ?
72
            $parameters['base_template'] :
73
            $this->getBaseTemplate();
74
75
        $parameters['admin_pool'] = $this->get('sonata.admin.pool');
76
77
        return parent::render($view, $parameters, $response);
78
    }
79
80
    /**
81
     * List action.
82
     *
83
     * @return Response
84
     *
85
     * @throws AccessDeniedException If access is not granted
86
     */
87
    public function listAction()
88
    {
89
        $request = $this->getRequest();
90
91
        $this->admin->checkAccess('list');
92
93
        $preResponse = $this->preList($request);
94
        if ($preResponse !== null) {
95
            return $preResponse;
96
        }
97
98
        if ($listMode = $request->get('_list_mode')) {
99
            $this->admin->setListMode($listMode);
100
        }
101
102
        $datagrid = $this->admin->getDatagrid();
103
        $formView = $datagrid->getForm()->createView();
104
105
        // set the theme for the current Admin Form
106
        $this->setFormTheme($formView, $this->admin->getFilterTheme());
0 ignored issues
show
Documentation introduced by
$this->admin->getFilterTheme() is of type array, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

Loading history...
165
    {
166
        $request = $this->getRequest();
167
        $id = $request->get($this->admin->getIdParameter());
168
        $object = $this->admin->getObject($id);
169
170
        if (!$object) {
171
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
172
        }
173
174
        $this->admin->checkAccess('delete', $object);
175
176
        $preResponse = $this->preDelete($request, $object);
177
        if ($preResponse !== null) {
178
            return $preResponse;
179
        }
180
181
        if ($this->getRestMethod() == 'DELETE') {
182
            // check the csrf token
183
            $this->validateCsrfToken('sonata.delete');
184
185
            $objectName = $this->admin->toString($object);
186
187
            try {
188
                $this->admin->delete($object);
189
190
                if ($this->isXmlHttpRequest()) {
191
                    return $this->renderJson(['result' => 'ok'], 200, []);
192
                }
193
194
                $this->addFlash(
195
                    'sonata_flash_success',
196
                    $this->trans(
197
                        'flash_delete_success',
198
                        ['%name%' => $this->escapeHtml($objectName)],
199
                        'SonataAdminBundle'
200
                    )
201
                );
202
            } catch (ModelManagerException $e) {
203
                $this->handleModelManagerException($e);
204
205
                if ($this->isXmlHttpRequest()) {
206
                    return $this->renderJson(['result' => 'error'], 200, []);
207
                }
208
209
                $this->addFlash(
210
                    'sonata_flash_error',
211
                    $this->trans(
212
                        'flash_delete_error',
213
                        ['%name%' => $this->escapeHtml($objectName)],
214
                        'SonataAdminBundle'
215
                    )
216
                );
217
            }
218
219
            return $this->redirectTo($object);
220
        }
221
222
        return $this->render($this->admin->getTemplate('delete'), [
223
            'object' => $object,
224
            'action' => 'delete',
225
            'csrf_token' => $this->getCsrfToken('sonata.delete'),
226
        ], null);
227
    }
228
229
    /**
230
     * Edit action.
231
     *
232
     * @param int|string|null $id
233
     *
234
     * @return Response|RedirectResponse
235
     *
236
     * @throws NotFoundHttpException If the object does not exist
237
     * @throws AccessDeniedException If access is not granted
238
     */
239
    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...
240
    {
241
        $request = $this->getRequest();
242
        // the key used to lookup the template
243
        $templateKey = 'edit';
244
245
        $id = $request->get($this->admin->getIdParameter());
246
        $existingObject = $this->admin->getObject($id);
247
248
        if (!$existingObject) {
249
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
250
        }
251
252
        $this->admin->checkAccess('edit', $existingObject);
253
254
        $preResponse = $this->preEdit($request, $existingObject);
255
        if ($preResponse !== null) {
256
            return $preResponse;
257
        }
258
259
        $this->admin->setSubject($existingObject);
260
        $objectId = $this->admin->getNormalizedIdentifier($existingObject);
261
262
        /** @var $form Form */
263
        $form = $this->admin->getForm();
264
        $form->setData($existingObject);
265
        $form->handleRequest($request);
266
267
        if ($form->isSubmitted()) {
268
            //TODO: remove this check for 4.0
269
            if (method_exists($this->admin, 'preValidate')) {
270
                $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...
271
            }
272
            $isFormValid = $form->isValid();
273
274
            // persist if the form was valid and if in preview mode the preview was approved
275
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
276
                $submittedObject = $form->getData();
277
                $this->admin->setSubject($submittedObject);
278
279
                try {
280
                    $existingObject = $this->admin->update($submittedObject);
281
282
                    if ($this->isXmlHttpRequest()) {
283
                        return $this->renderJson([
284
                            'result' => 'ok',
285
                            'objectId' => $objectId,
286
                            'objectName' => $this->escapeHtml($this->admin->toString($existingObject)),
287
                        ], 200, []);
288
                    }
289
290
                    $this->addFlash(
291
                        'sonata_flash_success',
292
                        $this->trans(
293
                            'flash_edit_success',
294
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
295
                            'SonataAdminBundle'
296
                        )
297
                    );
298
299
                    // redirect to edit mode
300
                    return $this->redirectTo($existingObject);
301
                } catch (ModelManagerException $e) {
302
                    $this->handleModelManagerException($e);
303
304
                    $isFormValid = false;
305
                } catch (LockException $e) {
306
                    $this->addFlash('sonata_flash_error', $this->trans('flash_lock_error', [
307
                        '%name%' => $this->escapeHtml($this->admin->toString($existingObject)),
308
                        '%link_start%' => '<a href="'.$this->admin->generateObjectUrl('edit', $existingObject).'">',
309
                        '%link_end%' => '</a>',
310
                    ], 'SonataAdminBundle'));
311
                }
312
            }
313
314
            // show an error message if the form failed validation
315
            if (!$isFormValid) {
316
                if (!$this->isXmlHttpRequest()) {
317
                    $this->addFlash(
318
                        'sonata_flash_error',
319
                        $this->trans(
320
                            'flash_edit_error',
321
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
322
                            'SonataAdminBundle'
323
                        )
324
                    );
325
                }
326
            } elseif ($this->isPreviewRequested()) {
327
                // enable the preview template if the form was valid and preview was requested
328
                $templateKey = 'preview';
329
                $this->admin->getShow();
330
            }
331
        }
332
333
        $formView = $form->createView();
334
        // set the theme for the current Admin Form
335
        $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...
336
337
        return $this->render($this->admin->getTemplate($templateKey), [
338
            'action' => 'edit',
339
            'form' => $formView,
340
            'object' => $existingObject,
341
            'objectId' => $objectId,
342
        ], null);
343
    }
344
345
    /**
346
     * Batch action.
347
     *
348
     * @return Response|RedirectResponse
349
     *
350
     * @throws NotFoundHttpException If the HTTP method is not POST
351
     * @throws \RuntimeException     If the batch action is not defined
352
     */
353
    public function batchAction()
354
    {
355
        $request = $this->getRequest();
356
        $restMethod = $this->getRestMethod();
357
358
        if ('POST' !== $restMethod) {
359
            throw $this->createNotFoundException(sprintf('Invalid request type "%s", POST expected', $restMethod));
360
        }
361
362
        // check the csrf token
363
        $this->validateCsrfToken('sonata.batch');
364
365
        $confirmation = $request->get('confirmation', false);
366
367
        if ($data = json_decode($request->get('data'), true)) {
368
            $action = $data['action'];
369
            $idx = $data['idx'];
370
            $allElements = $data['all_elements'];
371
            $request->request->replace(array_merge($request->request->all(), $data));
372
        } else {
373
            $request->request->set('idx', $request->get('idx', []));
374
            $request->request->set('all_elements', $request->get('all_elements', false));
375
376
            $action = $request->get('action');
377
            $idx = $request->get('idx');
378
            $allElements = $request->get('all_elements');
379
            $data = $request->request->all();
380
381
            unset($data['_sonata_csrf_token']);
382
        }
383
384
        // NEXT_MAJOR: Remove reflection check.
385
        $reflector = new \ReflectionMethod($this->admin, 'getBatchActions');
386
        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...
387
            @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...
388
                .' is deprecated since version 3.2.'
389
                .' Use Sonata\AdminBundle\Admin\AbstractAdmin::configureBatchActions instead.'
390
                .' The method will be final in 4.0.', E_USER_DEPRECATED
391
            );
392
        }
393
        $batchActions = $this->admin->getBatchActions();
394
        if (!array_key_exists($action, $batchActions)) {
395
            throw new \RuntimeException(sprintf('The `%s` batch action is not defined', $action));
396
        }
397
398
        $camelizedAction = Inflector::classify($action);
399
        $isRelevantAction = sprintf('batchAction%sIsRelevant', $camelizedAction);
400
401
        if (method_exists($this, $isRelevantAction)) {
402
            $nonRelevantMessage = call_user_func([$this, $isRelevantAction], $idx, $allElements, $request);
403
        } else {
404
            $nonRelevantMessage = count($idx) != 0 || $allElements; // at least one item is selected
405
        }
406
407
        if (!$nonRelevantMessage) { // default non relevant message (if false of null)
408
            $nonRelevantMessage = 'flash_batch_empty';
409
        }
410
411
        $datagrid = $this->admin->getDatagrid();
412
        $datagrid->buildPager();
413
414
        if (true !== $nonRelevantMessage) {
415
            $this->addFlash(
416
                'sonata_flash_info',
417
                $this->trans($nonRelevantMessage, [], 'SonataAdminBundle')
418
            );
419
420
            return new RedirectResponse(
421
                $this->admin->generateUrl(
422
                    'list',
423
                    ['filter' => $this->admin->getFilterParameters()]
424
                )
425
            );
426
        }
427
428
        $askConfirmation = isset($batchActions[$action]['ask_confirmation']) ?
429
            $batchActions[$action]['ask_confirmation'] :
430
            true;
431
432
        if ($askConfirmation && $confirmation != 'ok') {
433
            $actionLabel = $batchActions[$action]['label'];
434
            $batchTranslationDomain = isset($batchActions[$action]['translation_domain']) ?
435
                $batchActions[$action]['translation_domain'] :
436
                $this->admin->getTranslationDomain();
437
438
            $formView = $datagrid->getForm()->createView();
439
            $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...
440
441
            return $this->render($this->admin->getTemplate('batch_confirmation'), [
442
                'action' => 'list',
443
                'action_label' => $actionLabel,
444
                'batch_translation_domain' => $batchTranslationDomain,
445
                'datagrid' => $datagrid,
446
                'form' => $formView,
447
                'data' => $data,
448
                'csrf_token' => $this->getCsrfToken('sonata.batch'),
449
            ], null);
450
        }
451
452
        // execute the action, batchActionXxxxx
453
        $finalAction = sprintf('batchAction%s', $camelizedAction);
454
        if (!is_callable([$this, $finalAction])) {
455
            throw new \RuntimeException(sprintf('A `%s::%s` method must be callable', get_class($this), $finalAction));
456
        }
457
458
        $query = $datagrid->getQuery();
459
460
        $query->setFirstResult(null);
461
        $query->setMaxResults(null);
462
463
        $this->admin->preBatchAction($action, $query, $idx, $allElements);
464
465
        if (count($idx) > 0) {
466
            $this->admin->getModelManager()->addIdentifiersToQuery($this->admin->getClass(), $query, $idx);
467
        } elseif (!$allElements) {
468
            $query = null;
469
        }
470
471
        return call_user_func([$this, $finalAction], $query, $request);
472
    }
473
474
    /**
475
     * Create action.
476
     *
477
     * @return Response
478
     *
479
     * @throws AccessDeniedException If access is not granted
480
     */
481
    public function createAction()
482
    {
483
        $request = $this->getRequest();
484
        // the key used to lookup the template
485
        $templateKey = 'edit';
486
487
        $this->admin->checkAccess('create');
488
489
        $class = new \ReflectionClass($this->admin->hasActiveSubClass() ? $this->admin->getActiveSubClass() : $this->admin->getClass());
490
491
        if ($class->isAbstract()) {
492
            return $this->render(
493
                'SonataAdminBundle:CRUD:select_subclass.html.twig',
494
                [
495
                    'base_template' => $this->getBaseTemplate(),
496
                    'admin' => $this->admin,
497
                    'action' => 'create',
498
                ],
499
                null,
500
                $request
0 ignored issues
show
Unused Code introduced by
The call to CRUDController::render() has too many arguments starting with $request.

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

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

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

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