Completed
Push — dev ( 109b2b...7d432e )
by Arnaud
03:06
created

Admin::isActionGranted()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5.0592

Importance

Changes 3
Bugs 1 Features 1
Metric Value
c 3
b 1
f 1
dl 0
loc 23
ccs 13
cts 15
cp 0.8667
rs 8.5906
cc 5
eloc 11
nc 2
nop 2
crap 5.0592
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 12
    public function __construct(
102
        $name,
103
        DataProviderInterface $dataProvider,
104
        AdminConfiguration $configuration,
105
        MessageHandlerInterface $messageHandler
106
    ) {
107 12
        $this->name = $name;
108 12
        $this->dataProvider = $dataProvider;
109 12
        $this->configuration = $configuration;
110 12
        $this->messageHandler = $messageHandler;
111 12
        $this->entities = new ArrayCollection();
112 12
    }
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 2
    public function handleRequest(Request $request, $user = null)
123
    {
124
        // set current action
125 2
        $this->currentAction = $this->getAction($request->get('_route_params')['_action']);
126
        // check if user is logged have required permissions to get current action
127 2
        $this->checkPermissions($user);
128
129
        // criteria filter request
130 2
        $filter = new RequestFilter($this->currentAction->getConfiguration()->getCriteria());
131 2
        $criteriaFilter = $filter->filter($request);
132
133
        // pager filter request
134 2
        if ($this->currentAction->getConfiguration()->getPager() == 'pagerfanta') {
135 1
            $filter = new PagerfantaFilter();
136 1
            $pagerFilter = $filter->filter($request);
137 1
        } else {
138
            // empty bag
139 2
            $pagerFilter = new ParameterBag();
140
        }
141
142
        // if load strategy is none, no entity should be loaded
143 2
        if ($this->currentAction->getConfiguration()->getLoadStrategy() == Admin::LOAD_STRATEGY_NONE) {
144 1
            return;
145
        }
146
147
        // load entities according to action and request
148 2
        $this->load(
149 2
            $criteriaFilter->all(),
150 2
            $pagerFilter->get('order', []),
151 2
            $this->configuration->getMaxPerPage(),
152 2
            $pagerFilter->get('page', 1)
153 2
        );
154 2
    }
155
156
    /**
157
     * Check if user is allowed to be here
158
     *
159
     * @param UserInterface|string $user
160
     * @throws Exception
161
     */
162 2
    public function checkPermissions($user)
163
    {
164 2
        if (!($user instanceof UserInterface)) {
165 2
            return;
166
        }
167 1
        if ($this->currentAction === null) {
168 1
            throw new Exception('Current action should be set before checking the permissions');
169
        }
170 1
        $roles = $user->getRoles();
171 1
        $actionName = $this
172 1
            ->getCurrentAction()
173 1
            ->getName();
174
175 1
        if (!$this->isActionGranted($actionName, $roles)) {
176 1
            $message = sprintf('User with roles %s not allowed for action "%s"',
177 1
                implode(', ', $roles),
178
                $actionName
179 1
            );
180 1
            throw new NotFoundHttpException($message);
181
        }
182 1
    }
183
184
    /**
185
     * Create and return a new entity.
186
     *
187
     * @return object
188
     */
189 3
    public function create()
190
    {
191
        // create an entity from the data provider
192 3
        $entity = $this
193
            ->dataProvider
194 3
            ->create();
195
196
        // add it to the collection
197 3
        $this
198
            ->entities
199 3
            ->add($entity);
200
201 3
        return $entity;
202
    }
203
204
    /**
205
     * Save entity via admin manager. Error are catch, logged and a flash message is added to session
206
     *
207
     * @return bool true if the entity was saved without errors
208
     */
209 1 View Code Duplication
    public function save()
210
    {
211
        try {
212 1
            foreach ($this->entities as $entity) {
213 1
                $this
214
                    ->dataProvider
215 1
                    ->save($entity);
216 1
            }
217
            // inform user everything went fine
218 1
            $this
219
                ->messageHandler
220 1
                ->handleSuccess('lag.admin.' . $this->name . '.saved');
221 1
            $success = true;
222 1
        } catch (Exception $e) {
223 1
            $this
224
                ->messageHandler
225 1
                ->handleError(
226 1
                    'lag.admin.saved_errors',
227 1
                    "An error has occurred while saving an entity : {$e->getMessage()}, stackTrace: {$e->getTraceAsString()}"
228 1
                );
229 1
            $success = false;
230
        }
231 1
        return $success;
232
    }
233
234
    /**
235
     * Remove an entity with data provider
236
     *
237
     * @return bool true if the entity was saved without errors
238
     */
239 1 View Code Duplication
    public function remove()
240
    {
241
        try {
242 1
            foreach ($this->entities as $entity) {
243 1
                $this
244
                    ->dataProvider
245 1
                    ->remove($entity);
246 1
            }
247
            // inform user everything went fine
248 1
            $this
249
                ->messageHandler
250 1
                ->handleSuccess('lag.admin.' . $this->name . '.deleted');
251 1
            $success = true;
252 1
        } catch (Exception $e) {
253 1
            $this
254
                ->messageHandler
255 1
                ->handleError(
256 1
                    'lag.admin.deleted_errors',
257 1
                    "An error has occurred while deleting an entity : {$e->getMessage()}, stackTrace: {$e->getTraceAsString()} "
258 1
                );
259 1
            $success = false;
260
        }
261 1
        return $success;
262
    }
263
264
    /**
265
     * Generate a route for admin and action name (like lag.admin.my_admin)
266
     *
267
     * @param $actionName
268
     *
269
     * @return string
270
     *
271
     * @throws Exception
272
     */
273 1
    public function generateRouteName($actionName)
274
    {
275 1
        if (!array_key_exists($actionName, $this->getConfiguration()->getActions())) {
276 1
            $message = 'Invalid action name %s for admin %s (available action are: %s)';
277 1
            throw new Exception(sprintf($message, $actionName, $this->getName(), implode(', ', $this->getActionNames())));
278
        }
279
        // get routing name pattern
280 1
        $routingPattern = $this->getConfiguration()->getRoutingNamePattern();
281
        // replace admin and action name in pattern
282 1
        $routeName = str_replace('{admin}', Container::underscore($this->getName()), $routingPattern);
283 1
        $routeName = str_replace('{action}', $actionName, $routeName);
284
285 1
        return $routeName;
286
    }
287
288
    /**
289
     * Load entities manually according to criteria.
290
     *
291
     * @param array $criteria
292
     * @param array $orderBy
293
     * @param int $limit
294
     * @param int $offset
295
     */
296 2
    public function load(array $criteria, $orderBy = [], $limit = 25, $offset = 1)
297
    {
298 2
        $pager = $this
299 2
            ->getCurrentAction()
300 2
            ->getConfiguration()
301 2
            ->getPager();
302
303 2
        if ($pager == 'pagerfanta') {
304
            // adapter to pager fanta
305 1
            $adapter = new PagerFantaAdminAdapter($this->dataProvider, $criteria, $orderBy);
306
            // create pager
307 1
            $this->pager = new Pagerfanta($adapter);
308 1
            $this->pager->setMaxPerPage($limit);
309 1
            $this->pager->setCurrentPage($offset);
310
311 1
            $entities = $this
312
                ->pager
313 1
                ->getCurrentPageResults();
314 1
        } else {
315 2
            $entities = $this
316
                ->dataProvider
317 2
                ->findBy($criteria, $orderBy, $limit, $offset);
318
        }
319 2
        if (is_array($entities)) {
320
            $entities = new ArrayCollection($entities);
321
        }
322 2
        $this->entities = $entities;
323 2
    }
324
325
    /**
326
     * Return loaded entities
327
     *
328
     * @return mixed
329
     */
330
    public function getEntities()
331
    {
332
        return $this->entities;
333
    }
334
335
    /**
336
     * Return entity for current admin. If entity does not exist, it throws an exception.
337
     *
338
     * @return mixed
339
     *
340
     * @throws Exception
341
     */
342
    public function getUniqueEntity()
343
    {
344
        if ($this->entities->count() == 0) {
345
            throw new Exception("Entity not found in admin \"{$this->getName()}\".");
346
        }
347
        if ($this->entities->count() > 1) {
348
            throw new Exception("Too much entities found in admin \"{$this->getName()}\".");
349
        }
350
        return $this->entities->first();
351
    }
352
353
    /**
354
     * Return admin name
355
     *
356
     * @return string
357
     */
358 9
    public function getName()
359
    {
360 9
        return $this->name;
361
    }
362
363
    /**
364
     * Return true if current action is granted for user.
365
     *
366
     * @param string $actionName Le plus grand de tous les héros
367
     * @param array $roles
368
     *
369
     * @return bool
370
     */
371 1
    public function isActionGranted($actionName, array $roles)
372
    {
373 1
        $isGranted = array_key_exists($actionName, $this->actions);
374
375
        // if action exists
376 1
        if ($isGranted) {
377 1
            $isGranted = false;
378
            /** @var Action $action */
379 1
            $action = $this->actions[$actionName];
380
            // checking roles permissions
381 1
            foreach ($roles as $role) {
382
383 1
                if ($role instanceof Role) {
384
                    $role = $role->getRole();
385
                }
386 1
                if (in_array($role, $action->getPermissions())) {
387 1
                    $isGranted = true;
388 1
                }
389 1
            }
390 1
        }
391
392 1
        return $isGranted;
393
    }
394
395
    /**
396
     * @return ActionInterface[]
397
     */
398 3
    public function getActions()
399
    {
400 3
        return $this->actions;
401
    }
402
403
    /**
404
     * @return array
405
     */
406 1
    public function getActionNames()
407
    {
408 1
        return array_keys($this->actions);
409
    }
410
411
    /**
412
     * @param $name
413
     * @return ActionInterface
414
     * @throws Exception
415
     */
416 2
    public function getAction($name)
417
    {
418 2
        if (!array_key_exists($name, $this->getActions())) {
419 1
            throw new Exception(
420 1
                "Invalid action name \"{$name}\" for admin '{$this->getName()}'. Check your configuration"
421 1
            );
422
        }
423
424 2
        return $this->actions[$name];
425
    }
426
427
    /**
428
     * Return if an action with specified name exists form this admin.
429
     *
430
     * @param $name
431
     * @return bool
432
     */
433
    public function hasAction($name)
434
    {
435
        return array_key_exists($name, $this->actions);
436
    }
437
438
    /**
439
     * @param ActionInterface $action
440
     * @return void
441
     */
442 8
    public function addAction(ActionInterface $action)
443
    {
444 8
        $this->actions[$action->getName()] = $action;
445 8
    }
446
447
    /**
448
     * @return ActionInterface
449
     */
450 2
    public function getCurrentAction()
451
    {
452 2
        return $this->currentAction;
453
    }
454
455
    /**
456
     * Return admin configuration object
457
     *
458
     * @return AdminConfiguration
459
     */
460 9
    public function getConfiguration()
461
    {
462 9
        return $this->configuration;
463
    }
464
}
465