Completed
Push — dev ( 3e1fc7...a387b8 )
by Arnaud
04:24 queued 49s
created

Admin::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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