Completed
Branch dev (a083dd)
by Arnaud
03:08
created

Admin::isActionGranted()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 3
Bugs 1 Features 1
Metric Value
c 3
b 1
f 1
dl 0
loc 23
ccs 0
cts 11
cp 0
rs 8.5906
cc 5
eloc 11
nc 2
nop 2
crap 30
1
<?php
2
3
namespace LAG\AdminBundle\Admin;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use LAG\AdminBundle\Admin\Behaviors\AdminTrait;
7
use LAG\AdminBundle\Admin\Configuration\AdminConfiguration;
8
use Doctrine\Common\Collections\ArrayCollection;
9
use Exception;
10
use LAG\AdminBundle\DataProvider\DataProviderInterface;
11
use LAG\AdminBundle\Exception\AdminException;
12
use LAG\AdminBundle\Filter\PagerfantaFilter;
13
use LAG\AdminBundle\Filter\RequestFilter;
14
use LAG\AdminBundle\Message\MessageHandlerInterface;
15
use LAG\AdminBundle\Pager\PagerFantaAdminAdapter;
16
use Pagerfanta\Pagerfanta;
17
use Symfony\Component\DependencyInjection\Container;
18
use Symfony\Component\HttpFoundation\ParameterBag;
19
use Symfony\Component\HttpFoundation\Request;
20
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
21
use Symfony\Component\Security\Core\Role\Role;
22
use Symfony\Component\Security\Core\User\UserInterface;
23
24
class Admin implements AdminInterface
25
{
26
    use AdminTrait;
27
28
    /**
29
     * Do not load entities on handleRequest (for create method for example)
30
     */
31
    const LOAD_STRATEGY_NONE = 'strategy_none';
32
33
    /**
34
     * Load one entity on handleRequest (edit method for example)
35
     */
36
    const LOAD_STRATEGY_UNIQUE = 'strategy_unique';
37
38
    /**
39
     * Load multiple entities on handleRequest (list method for example)
40
     */
41
    const LOAD_STRATEGY_MULTIPLE = 'strategy_multiple';
42
43
    /**
44
     * Entities collection.
45
     *
46
     * @var ArrayCollection
47
     */
48
    protected $entities;
49
50
    /**
51
     * @var MessageHandlerInterface
52
     */
53
    protected $messageHandler;
54
55
    /**
56
     * @var EntityManagerInterface
57
     */
58
    protected $entityManager;
59
60
    /**
61
     * @var DataProviderInterface
62
     */
63
    protected $dataProvider;
64
65
    /**
66
     * Admin configuration object
67
     *
68
     * @var AdminConfiguration
69
     */
70
    protected $configuration;
71
72
    /**
73
     * Admin configured actions
74
     *
75
     * @var ActionInterface[]
76
     */
77
    protected $actions = [];
78
79
    /**
80
     * Admin current action. It will be set after calling the handleRequest()
81
     *
82
     * @var ActionInterface
83
     */
84
    protected $currentAction;
85
86
    /**
87
     * Admin name
88
     *
89
     * @var string
90
     */
91
    protected $name;
92
93
    /**
94
     * Admin constructor.
95
     *
96
     * @param string $name
97
     * @param DataProviderInterface $dataProvider
98
     * @param AdminConfiguration $configuration
99
     * @param MessageHandlerInterface $messageHandler
100
     */
101 6
    public function __construct(
102
        $name,
103
        DataProviderInterface $dataProvider,
104
        AdminConfiguration $configuration,
105
        MessageHandlerInterface $messageHandler
106
    ) {
107 6
        $this->name = $name;
108 6
        $this->dataProvider = $dataProvider;
109 6
        $this->configuration = $configuration;
110 6
        $this->messageHandler = $messageHandler;
111 6
        $this->entities = new ArrayCollection();
112 6
    }
113
114
    /**
115
     * Load entities and set current action according to request
116
     *
117
     * @param Request $request
118
     * @param null $user
119
     * @return void
120
     * @throws AdminException
121
     */
122
    public function handleRequest(Request $request, $user = null)
123
    {
124
        // set current action
125
        $this->currentAction = $this->getAction($request->get('_route_params')['_action']);
126
        // check if user is logged have required permissions to get current action
127
        $this->checkPermissions($user);
128
129
        // criteria filter request
130
        $filter = new RequestFilter($this->currentAction->getConfiguration()->getCriteria());
131
        $criteriaFilter = $filter->filter($request);
132
133
        // pager filter request
134
        if ($this->currentAction->getConfiguration()->getPager() == 'pagerfanta') {
135
            $filter = new PagerfantaFilter();
136
            $pagerFilter = $filter->filter($request);
137
        } else {
138
            // empty bag
139
            $pagerFilter = new ParameterBag();
140
        }
141
142
        // if load strategy is none, no entity should be loaded
143
        if ($this->currentAction->getConfiguration()->getLoadStrategy() == Admin::LOAD_STRATEGY_NONE) {
144
            return;
145
        }
146
147
        // load entities according to action and request
148
        $this->load(
149
            $criteriaFilter->all(),
150
            $pagerFilter->get('order', []),
151
            $this->configuration->getMaxPerPage(),
152
            $pagerFilter->get('page', 1)
153
        );
154
    }
155
156
    /**
157
     * Check if user is allowed to be here
158
     *
159
     * @param UserInterface|string $user
160
     */
161
    public function checkPermissions($user)
162
    {
163
        if (!($user instanceof UserInterface)) {
164
            return;
165
        }
166
        $roles = $user->getRoles();
167
        $actionName = $this
168
            ->getCurrentAction()
169
            ->getName();
170
171
        if (!$this->isActionGranted($actionName, $roles)) {
172
            $message = sprintf('User with roles %s not allowed for action "%s"',
173
                implode(', ', $roles),
174
                $actionName
175
            );
176
            throw new NotFoundHttpException($message);
177
        }
178
    }
179
180
    /**
181
     * Create and return a new entity.
182
     *
183
     * @return object
184
     */
185
    public function create()
186
    {
187
        // create an entity from the data provider
188
        $entity = $this
189
            ->dataProvider
190
            ->create();
191
192
        // add it to the collection
193
        $this
194
            ->entities
195
            ->add($entity);
196
197
        return $entity;
198
    }
199
200
    /**
201
     * Save entity via admin manager. Error are catch, logged and a flash message is added to session
202
     *
203
     * @return bool true if the entity was saved without errors
204
     */
205 View Code Duplication
    public function save()
206
    {
207
        try {
208
            foreach ($this->entities as $entity) {
209
                $this
210
                    ->dataProvider
211
                    ->save($entity);
212
            }
213
            // inform user everything went fine
214
            $this
215
                ->messageHandler
216
                ->handleSuccess('lag.admin.' . $this->name . '.saved');
217
            $success = true;
218
        } catch (Exception $e) {
219
            $this
220
                ->messageHandler
221
                ->handleError(
222
                    'lag.admin.saved_errors',
223
                    "An error has occurred while saving an entity : {$e->getMessage()}, stackTrace: {$e->getTraceAsString()}"
224
                );
225
            $success = false;
226
        }
227
        return $success;
228
    }
229
230
    /**
231
     * Remove an entity with data provider
232
     *
233
     * @return bool true if the entity was saved without errors
234
     */
235 View Code Duplication
    public function remove()
236
    {
237
        try {
238
            foreach ($this->entities as $entity) {
239
                $this
240
                    ->dataProvider
241
                    ->remove($entity);
242
            }
243
            // inform user everything went fine
244
            $this
245
                ->messageHandler
246
                ->handleSuccess('lag.admin.' . $this->name . '.deleted');
247
            $success = true;
248
        } catch (Exception $e) {
249
            $this
250
                ->messageHandler
251
                ->handleError(
252
                    'lag.admin.deleted_errors',
253
                    "An error has occurred while deleting an entity : {$e->getMessage()}, stackTrace: {$e->getTraceAsString()} "
254
                );
255
            $success = false;
256
        }
257
        return $success;
258
    }
259
260
    /**
261
     * Generate a route for admin and action name (like lag.admin.my_admin)
262
     *
263
     * @param $actionName
264
     *
265
     * @return string
266
     *
267
     * @throws Exception
268
     */
269
    public function generateRouteName($actionName)
270
    {
271
        if (!array_key_exists($actionName, $this->getConfiguration()->getActions())) {
272
            $message = 'Invalid action name %s for admin %s (available action are: %s)';
273
            throw new Exception(sprintf($message, $actionName, $this->getName(), implode(', ', $this->getActionNames())));
274
        }
275
        // get routing name pattern
276
        $routingPattern = $this->getConfiguration()->getRoutingNamePattern();
277
        // replace admin and action name in pattern
278
        $routeName = str_replace('{admin}', Container::underscore($this->getName()), $routingPattern);
279
        $routeName = str_replace('{action}', $actionName, $routeName);
280
281
        return $routeName;
282
    }
283
284
    /**
285
     * Load entities manually according to criteria.
286
     *
287
     * @param array $criteria
288
     * @param array $orderBy
289
     * @param int $limit
290
     * @param int $offset
291
     */
292
    public function load(array $criteria, $orderBy = [], $limit = 25, $offset = 1)
293
    {
294
        $pager = $this
295
            ->getCurrentAction()
296
            ->getConfiguration()
297
            ->getPager();
298
299
        if ($pager == 'pagerfanta') {
300
            // adapter to pager fanta
301
            $adapter = new PagerFantaAdminAdapter($this->dataProvider, $criteria, $orderBy);
302
            // create pager
303
            $this->pager = new Pagerfanta($adapter);
304
            $this->pager->setMaxPerPage($limit);
305
            $this->pager->setCurrentPage($offset);
306
307
            $entities = $this
308
                ->pager
309
                ->getCurrentPageResults();
310
        } else {
311
            $entities = $this
312
                ->dataProvider
313
                ->findBy($criteria, $orderBy, $limit, $offset);
314
        }
315
        if (is_array($entities)) {
316
            $entities = new ArrayCollection($entities);
317
        }
318
        $this->entities = $entities;
319
    }
320
321
    /**
322
     * Return loaded entities
323
     *
324
     * @return mixed
325
     */
326
    public function getEntities()
327
    {
328
        return $this->entities;
329
    }
330
331
    /**
332
     * Return entity for current admin. If entity does not exist, it throws an exception.
333
     *
334
     * @return mixed
335
     *
336
     * @throws Exception
337
     */
338
    public function getUniqueEntity()
339
    {
340
        if ($this->entities->count() == 0) {
341
            throw new Exception("Entity not found in admin \"{$this->getName()}\".");
342
        }
343
        if ($this->entities->count() > 1) {
344
            throw new Exception("Too much entities found in admin \"{$this->getName()}\".");
345
        }
346
        return $this->entities->first();
347
    }
348
349
    /**
350
     * Return admin name
351
     *
352
     * @return string
353
     */
354 6
    public function getName()
355
    {
356 6
        return $this->name;
357
    }
358
359
    /**
360
     * Return true if current action is granted for user.
361
     *
362
     * @param string $actionName Le plus grand de tous les héros
363
     * @param array $roles
364
     *
365
     * @return bool
366
     */
367
    public function isActionGranted($actionName, array $roles)
368
    {
369
        $isGranted = array_key_exists($actionName, $this->actions);
370
371
        // if action exists
372
        if ($isGranted) {
373
            $isGranted = false;
374
            /** @var Action $action */
375
            $action = $this->actions[$actionName];
376
            // checking roles permissions
377
            foreach ($roles as $role) {
378
379
                if ($role instanceof Role) {
380
                    $role = $role->getRole();
381
                }
382
                if (in_array($role, $action->getPermissions())) {
383
                    $isGranted = true;
384
                }
385
            }
386
        }
387
388
        return $isGranted;
389
    }
390
391
    /**
392
     * @return ActionInterface[]
393
     */
394 1
    public function getActions()
395
    {
396 1
        return $this->actions;
397
    }
398
399
    /**
400
     * @return array
401
     */
402
    public function getActionNames()
403
    {
404
        return array_keys($this->actions);
405
    }
406
407
    /**
408
     * @param $name
409
     * @return ActionInterface
410
     * @throws Exception
411
     */
412
    public function getAction($name)
413
    {
414
        if (!array_key_exists($name, $this->getActions())) {
415
            throw new Exception("Invalid action name \"{$name}\" for admin '{$this->getName()}'");
416
        }
417
418
        return $this->actions[$name];
419
    }
420
421
    /**
422
     * Return if an action with specified name exists form this admin.
423
     *
424
     * @param $name
425
     * @return bool
426
     */
427
    public function hasAction($name)
428
    {
429
        return array_key_exists($name, $this->actions);
430
    }
431
432
    /**
433
     * @param ActionInterface $action
434
     * @return void
435
     */
436 6
    public function addAction(ActionInterface $action)
437
    {
438 6
        $this->actions[$action->getName()] = $action;
439 6
    }
440
441
    /**
442
     * @return ActionInterface
443
     */
444
    public function getCurrentAction()
445
    {
446
        return $this->currentAction;
447
    }
448
449
    /**
450
     * Return admin configuration object
451
     *
452
     * @return AdminConfiguration
453
     */
454 6
    public function getConfiguration()
455
    {
456 6
        return $this->configuration;
457
    }
458
}
459