You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

634 lines
23KB

  1. <?php
  2. namespace Lc\SovBundle\Controller;
  3. use Doctrine\ORM\EntityManagerInterface;
  4. use Doctrine\ORM\QueryBuilder;
  5. use EasyCorp\Bundle\EasyAdminBundle\Collection\ActionCollection;
  6. use EasyCorp\Bundle\EasyAdminBundle\Collection\EntityCollection;
  7. use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
  8. use EasyCorp\Bundle\EasyAdminBundle\Collection\FilterCollection;
  9. use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
  10. use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
  11. use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
  12. use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
  13. use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
  14. use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController as EaAbstractCrudController;
  15. use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
  16. use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
  17. use EasyCorp\Bundle\EasyAdminBundle\Event\AfterCrudActionEvent;
  18. use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityUpdatedEvent;
  19. use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeCrudActionEvent;
  20. use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityDeletedEvent;
  21. use EasyCorp\Bundle\EasyAdminBundle\Exception\ForbiddenActionException;
  22. use EasyCorp\Bundle\EasyAdminBundle\Exception\InsufficientEntityPermissionException;
  23. use EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory;
  24. use EasyCorp\Bundle\EasyAdminBundle\Factory\FilterFactory;
  25. use EasyCorp\Bundle\EasyAdminBundle\Factory\PaginatorFactory;
  26. use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;
  27. use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
  28. use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
  29. use EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider;
  30. use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
  31. use Lc\SovBundle\Doctrine\Extension\DevAliasInterface;
  32. use Lc\SovBundle\Doctrine\Extension\SeoInterface;
  33. use Lc\SovBundle\Doctrine\Extension\SortableInterface;
  34. use Lc\SovBundle\Doctrine\Extension\TranslatableInterface;
  35. use Lc\SovBundle\Doctrine\Extension\TreeInterface;
  36. use Lc\SovBundle\Field\CollectionField;
  37. use Lc\SovBundle\Form\Common\PositionType;
  38. use Lc\SovBundle\Translation\TranslatorAdmin;
  39. use Symfony\Component\Form\Extension\Core\Type\CollectionType;
  40. use Symfony\Component\Form\Extension\Core\Type\TextType;
  41. use Symfony\Component\HttpFoundation\RequestStack;
  42. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  43. abstract class AbstractAdminController extends EaAbstractCrudController
  44. {
  45. public static function getSubscribedServices()
  46. {
  47. return array_merge(
  48. parent::getSubscribedServices(),
  49. [
  50. 'session' => SessionInterface::class,
  51. 'request' => RequestStack::class,
  52. 'em' => EntityManagerInterface::class,
  53. 'translator_admin' => TranslatorAdmin::class,
  54. ]
  55. );
  56. }
  57. public function configureResponseParameters(KeyValueStore $responseParameters): KeyValueStore
  58. {
  59. $this->overrideGlobalActions($responseParameters->get('global_actions'));
  60. $this->overrideEntitiesActions($responseParameters->get('entities'));
  61. if (Crud::PAGE_INDEX === $responseParameters->get('pageName')) {
  62. $responseParameters->set('fields', $this->configureFields('index'));
  63. }
  64. return $responseParameters;
  65. }
  66. public function overrideEntitiesActions(?EntityCollection $entities): void
  67. {
  68. }
  69. public function overrideGlobalActions(?ActionCollection $actions): void
  70. {
  71. if ($actions) {
  72. $context = $this->get(AdminContextProvider::class)->getContext();
  73. $adminUrlGenerator = $this->get(AdminUrlGenerator::class);
  74. foreach ($actions as $i => $action) {
  75. //récriture du bouton 'retour au parent'
  76. if ($action->getName() == 'index_parent') {
  77. $entity = $context->getEntity()->getInstance();
  78. if ($entity !== null) {
  79. if ($entity->getParent() !== null) {
  80. $url = $adminUrlGenerator
  81. ->setController($context->getCrud()->getControllerFqcn())
  82. ->set('entityId', $entity->getParent()->getId())
  83. ->generateUrl();
  84. $action->setLinkUrl($url);
  85. }
  86. } else {
  87. unset($actions[$i]);
  88. }
  89. }
  90. if ($action->getName() == 'sort') {
  91. $entityId = $context->getRequest()->get('entityId');
  92. if ($entityId != null) {
  93. $url = $adminUrlGenerator
  94. ->setController($context->getCrud()->getControllerFqcn())
  95. ->setAction($action->getName())
  96. ->set('entityId', $entityId)
  97. ->generateUrl();
  98. $action->setLinkUrl($url);
  99. }
  100. }
  101. }
  102. }
  103. }
  104. public function configureCrud(Crud $crud): Crud
  105. {
  106. $crud = parent::configureCrud($crud);
  107. $this->setMaxResults($crud);
  108. if ($this->isInstanceOf(SortableInterface::class)) {
  109. $crud->setDefaultSort(['position' => 'ASC']);
  110. }
  111. return $crud;
  112. }
  113. public function setMaxResults(Crud $crud): void
  114. {
  115. $entityClass = $this->getEntityFqcn();
  116. $paramListMaxResults = 'listMaxResults';
  117. $paramSessionListMaxResults = $entityClass.'-'.$paramListMaxResults;
  118. $requestListMaxResults = $this->get('request')->getCurrentRequest()->get($paramListMaxResults);
  119. if ($requestListMaxResults) {
  120. $this->get('session')->set($paramSessionListMaxResults, $requestListMaxResults);
  121. }
  122. $maxResults = $this->get('session')->get($paramSessionListMaxResults) ? $this->get('session')->get(
  123. $paramSessionListMaxResults
  124. ) : 30;
  125. $crud->setPaginatorPageSize($maxResults);
  126. }
  127. public function configureFields(string $pageName): iterable
  128. {
  129. $seoPanel = $confPanel = array();
  130. if ($this->isInstanceOf(SeoInterface::class)) {
  131. $seoPanel = [
  132. FormField::addPanel('seo')->setTemplateName('crud/field/generic'),
  133. TextField::new('metaTitle')->setLabel('Meta Title')->setHelp(
  134. 'Affiché dans les résultats de recherche Google'
  135. )->hideOnIndex(),
  136. TextareaField::new('metaDescription')->setLabel('Meta description')->setHelp(
  137. 'Affiché dans les résultats de recherche Google'
  138. )->hideOnIndex(),
  139. CollectionField::new('oldUrls')
  140. ->setFormTypeOption('entry_type', TextType::class)->setLabel(
  141. 'Anciennes urls du document'
  142. )->hideOnIndex(),
  143. ];
  144. }
  145. if ($this->isInstanceOf(DevAliasInterface::class)) {
  146. $confPanel = [
  147. FormField::addPanel('configuration')->setTemplateName('crud/field/generic'),
  148. TextField::new('devAlias')->hideOnIndex(),
  149. ];
  150. }
  151. return array_merge($seoPanel, $confPanel);
  152. }
  153. public function sort(AdminContext $context)
  154. {
  155. $event = new BeforeCrudActionEvent($context);
  156. $this->get('event_dispatcher')->dispatch($event);
  157. if ($event->isPropagationStopped()) {
  158. return $event->getResponse();
  159. }
  160. //if (!$this->isGranted(Permission::EA_EXECUTE_ACTION) || !$this->isInstanceOf(SortableInterface::class)) {
  161. if (!$this->isInstanceOf(SortableInterface::class)) {
  162. throw new ForbiddenActionException($context);
  163. }
  164. $fields = FieldCollection::new($this->configureFields(Crud::PAGE_INDEX));
  165. $filters = $this->get(FilterFactory::class)->create(
  166. $context->getCrud()->getFiltersConfig(),
  167. $fields,
  168. $context->getEntity()
  169. );
  170. $queryBuilder = $this->createIndexQueryBuilder($context->getSearch(), $context->getEntity(), $fields, $filters);
  171. $paginator = $this->get(PaginatorFactory::class)->create($queryBuilder);
  172. $entities = $this->get(EntityFactory::class)->createCollection($context->getEntity(), $paginator->getResults());
  173. $this->get(EntityFactory::class)->processFieldsForAll($entities, $fields);
  174. $sortableForm = $this->createFormBuilder(array('entities', $paginator->getResults()))
  175. ->add(
  176. 'entities',
  177. CollectionType::class,
  178. array(
  179. 'required' => true,
  180. 'allow_add' => true,
  181. 'entry_type' => PositionType::class,
  182. )
  183. )
  184. ->getForm();
  185. $entityManager = $this->getDoctrine()->getManagerForClass($this->getEntityFqcn());
  186. $repository = $entityManager->getRepository($this->getEntityFqcn());
  187. $sortableForm->handleRequest($context->getRequest());
  188. if ($sortableForm->isSubmitted() && $sortableForm->isValid()) {
  189. foreach ($sortableForm->get('entities')->getData() as $elm) {
  190. $entityInstance = $repository->find($elm['id']);
  191. $entityDto = $context->getEntity()->newWithInstance($entityInstance);
  192. if (!$entityDto->isAccessible()) {
  193. throw new InsufficientEntityPermissionException($context);
  194. }
  195. $event = new BeforeEntityDeletedEvent($entityInstance);
  196. $this->get('event_dispatcher')->dispatch($event);
  197. $entityInstance = $event->getEntityInstance();
  198. $entityInstance->setPosition($elm['position']);
  199. $this->updateEntity($entityManager, $entityInstance);
  200. $this->get('event_dispatcher')->dispatch(new AfterEntityUpdatedEvent($entityInstance));
  201. }
  202. $url = $this->get(AdminUrlGenerator::class)
  203. ->setAction(Action::INDEX)
  204. ->generateUrl();
  205. $this->addFlash('success', $this->translatorAdmin->transFlashMessage('sort'), array());
  206. return $this->redirect($url);
  207. }
  208. $responseParameters = $this->configureResponseParameters(
  209. KeyValueStore::new(
  210. [
  211. 'pageName' => Crud::PAGE_INDEX,
  212. 'templatePath' => '@LcSov/adminlte/crud/sort.html.twig',
  213. 'entities' => $entities,
  214. 'paginator' => $paginator,
  215. 'global_actions' => array(),
  216. 'batch_actions' => array(),
  217. 'filters' => $filters,
  218. 'sortable_form' => $sortableForm,
  219. ]
  220. )
  221. );
  222. $responseParameters->set('fields', $this->configureFields('index'));
  223. $event = new AfterCrudActionEvent($context, $responseParameters);
  224. $this->get('event_dispatcher')->dispatch($event);
  225. if ($event->isPropagationStopped()) {
  226. return $event->getResponse();
  227. }
  228. return $responseParameters;
  229. }
  230. public function createIndexQueryBuilder(
  231. SearchDto $searchDto,
  232. EntityDto $entityDto,
  233. FieldCollection $fields,
  234. FilterCollection $filters
  235. ): QueryBuilder {
  236. $queryBuilder = parent::createIndexQueryBuilder(
  237. $searchDto,
  238. $entityDto,
  239. $fields,
  240. $filters
  241. );
  242. //TOdo utiliser les repositoryQuery ?
  243. if ($this->isInstanceOf(TreeInterface::class)) {
  244. $entityId = $searchDto->getRequest()->get('entityId');
  245. if ($entityId !== null) {
  246. $queryBuilder->andWhere('entity.parent = :entityId');
  247. $queryBuilder->setParameter('entityId', $searchDto->getRequest()->get('entityId'));
  248. } else {
  249. $queryBuilder->andWhere('entity.parent IS NULL');
  250. }
  251. }
  252. return $queryBuilder;
  253. }
  254. public function createSortQueryBuilder(
  255. SearchDto $searchDto,
  256. EntityDto $entityDto,
  257. FieldCollection $fields,
  258. FilterCollection $filters
  259. ): QueryBuilder {
  260. $queryBuilder = $this->createIndexQueryBuilder($searchDto, $entityDto, $fields, $filters);
  261. return $queryBuilder;
  262. }
  263. public function edit(AdminContext $context)
  264. {
  265. $response = parent::edit($context);;
  266. // on vide le flash bag si édition en ajax (notification déjà affichée en Javascript)
  267. if ($context->getRequest()->isXmlHttpRequest()) {
  268. $this->get('session')->getFlashBag()->clear();
  269. }
  270. return $response;
  271. }
  272. public function isInstanceOf(string $interfaceName): bool
  273. {
  274. return in_array($interfaceName, class_implements($this->getEntityFqcn()));
  275. }
  276. public function getControllerFqcnByInterface(string $interface): string
  277. {
  278. $context = $this->get(AdminContextProvider::class)->getContext();
  279. return $context->getCrudControllers()->findCrudFqcnByEntityFqcn(
  280. $this->get('em')->getEntityName($interface)
  281. );
  282. }
  283. public function createEntity(string $entityFqcn)
  284. {
  285. if(method_exists($this, 'getEntityFactory')) {
  286. $factoryClass = $this->getEntityFactory();
  287. $factory = new $factoryClass;
  288. return $factory->create();
  289. }
  290. else {
  291. return parent::createEntity($entityFqcn);
  292. }
  293. }
  294. public function updateEntity(EntityManagerInterface $entityManager, $entityInstance): void
  295. {
  296. $entityManager->update($entityInstance);
  297. $entityManager->flush();
  298. }
  299. public function persistEntity(EntityManagerInterface $entityManager, $entityInstance): void
  300. {
  301. $entityManager->create($entityInstance);
  302. $entityManager->flush();
  303. }
  304. public function configureActions(Actions $actions): Actions
  305. {
  306. $this->buildIndexActions($actions);
  307. $this->buildEditActions($actions);
  308. $this->buildDetailActions($actions);
  309. $this->buildNewActions($actions);
  310. $this->handleTranslatableEntityActions($actions);
  311. $this->handleSortableEntityActions($actions);
  312. $this->handleTreeEntityActions($actions);
  313. /*$actions->reorder(Crud::PAGE_EDIT, [Action::INDEX, Action::SAVE_AND_RETURN, Action::SAVE_AND_CONTINUE, Action::DELETE]);
  314. $actions->reorder(Crud::PAGE_NEW, [Action::INDEX, Action::SAVE_AND_RETURN, Action::SAVE_AND_ADD_ANOTHER]);*/
  315. return $actions;
  316. }
  317. public function buildIndexActions(Actions $actions): void
  318. {
  319. $this->actionUpdate(
  320. $actions,
  321. Crud::PAGE_INDEX,
  322. Action::NEW,
  323. [
  324. 'icon' => 'plus',
  325. 'label' => $this->get('translator_admin')->transAction('create'),
  326. 'add_class' => 'btn-sm',
  327. ]
  328. );
  329. $this->actionUpdate(
  330. $actions,
  331. Crud::PAGE_INDEX,
  332. Action::EDIT,
  333. [
  334. 'class' => 'btn btn-sm btn-primary',
  335. 'icon' => 'edit',
  336. 'label' => false,
  337. 'html_attributes' => array(
  338. 'data-toggle' => 'tooltip',
  339. 'title' => $this->get('translator_admin')->transAction('edit'),
  340. ),
  341. ]
  342. );
  343. $this->actionUpdate(
  344. $actions,
  345. Crud::PAGE_INDEX,
  346. Action::DETAIL,
  347. [
  348. 'icon' => 'eye',
  349. 'add_class' => 'btn btn-sm btn-success',
  350. 'label' => false,
  351. 'html_attributes' => array(
  352. 'data-toggle' => 'tooltip',
  353. 'title' => $this->get('translator_admin')->transAction('detail'),
  354. ),
  355. ]
  356. );
  357. $this->actionUpdate(
  358. $actions,
  359. Crud::PAGE_INDEX,
  360. Action::DELETE,
  361. [
  362. 'icon' => 'trash',
  363. 'dropdown' => true,
  364. 'label' => $this->get('translator_admin')->transAction('delete'),
  365. ]
  366. );
  367. $this->actionUpdate(
  368. $actions,
  369. Crud::PAGE_INDEX,
  370. Action::BATCH_DELETE,
  371. [
  372. 'class' => 'btn btn-sm btn-danger',
  373. 'icon' => 'trash',
  374. 'label' => $this->get('translator_admin')->transAction('delete'),
  375. ]
  376. );
  377. }
  378. public function buildEditActions(Actions $actions): void
  379. {
  380. $actions->add(Crud::PAGE_EDIT, Action::INDEX);
  381. $actions->add(Crud::PAGE_EDIT, Action::DELETE);
  382. $this->actionUpdate(
  383. $actions,
  384. Crud::PAGE_EDIT,
  385. Action::SAVE_AND_RETURN,
  386. [
  387. 'add_class' => 'float-right',
  388. 'icon' => 'check',
  389. 'label' => $this->get('translator_admin')->transAction('save_and_return'),
  390. ]
  391. );
  392. $this->actionUpdate(
  393. $actions,
  394. Crud::PAGE_EDIT,
  395. Action::INDEX,
  396. [
  397. 'icon' => 'chevron-left',
  398. 'class' => 'btn btn-link',
  399. 'label' => $this->get('translator_admin')->transAction('back_index'),
  400. ]
  401. );
  402. $this->actionUpdate(
  403. $actions,
  404. Crud::PAGE_EDIT,
  405. Action::SAVE_AND_CONTINUE,
  406. [
  407. 'class' => 'btn btn-info float-right',
  408. 'label' => $this->get('translator_admin')->transAction('save_and_continue'),
  409. ]
  410. );
  411. $this->actionUpdate(
  412. $actions,
  413. Crud::PAGE_EDIT,
  414. Action::DELETE,
  415. [
  416. 'icon' => 'trash',
  417. 'class' => 'btn btn-outline-danger action-delete',
  418. 'label' => $this->get('translator_admin')->transAction('delete'),
  419. ]
  420. );
  421. }
  422. public function buildDetailActions(Actions $actions): void
  423. {
  424. }
  425. public function buildNewActions(Actions $actions): void
  426. {
  427. $actions->add(Crud::PAGE_NEW, Action::INDEX);
  428. $this->actionUpdate(
  429. $actions,
  430. Crud::PAGE_EDIT,
  431. Action::SAVE_AND_RETURN,
  432. [
  433. 'add_class' => 'float-right',
  434. 'icon' => 'check',
  435. 'label' => $this->get('translator_admin')->transAction('save_and_return'),
  436. ]
  437. );
  438. $this->actionUpdate(
  439. $actions,
  440. Crud::PAGE_EDIT,
  441. Action::INDEX,
  442. [
  443. 'icon' => 'chevron-left',
  444. 'class' => 'btn btn-link',
  445. 'label' => $this->get('translator_admin')->transAction('back_index'),
  446. ]
  447. );
  448. $this->actionUpdate(
  449. $actions,
  450. Crud::PAGE_EDIT,
  451. Action::SAVE_AND_ADD_ANOTHER,
  452. [
  453. 'class' => 'btn btn-info float-right',
  454. 'label' => $this->get('translator_admin')->transAction('save_and_add_another'),
  455. ]
  456. );
  457. }
  458. public function handleTranslatableEntityActions(Actions $actions): void
  459. {
  460. if ($this->isInstanceOf(TranslatableInterface::class)) {
  461. $actions->update(
  462. Crud::PAGE_INDEX,
  463. Action::EDIT,
  464. function (Action $action) {
  465. $action->setTemplatePath('@LcSov/adminlte/crud/action/translatable.html.twig');
  466. return $action;
  467. }
  468. );
  469. }
  470. }
  471. public function handleSortableEntityActions(Actions $actions): void
  472. {
  473. if ($this->isInstanceOf(SortableInterface::class)) {
  474. $sortAction = Action::new('sort', $this->get('translator_admin')->transAction('sort'), 'fa fa-sort')
  475. ->linkToCrudAction('sort')
  476. ->setCssClass('btn btn-sm btn-success')
  477. ->createAsGlobalAction();
  478. $actions->add(Crud::PAGE_INDEX, $sortAction);
  479. }
  480. }
  481. public function handleTreeEntityActions(Actions $actions): void
  482. {
  483. if ($this->isInstanceOf(TreeInterface::class)) {
  484. $indexChildAction = Action::new(
  485. 'index_children',
  486. $this->get('translator_admin')->transAction('index_children'),
  487. 'fa fa-list'
  488. )
  489. ->linkToCrudAction(Action::INDEX)
  490. ->setLabel('')
  491. ->setHtmlAttributes(array('data-toggle' => 'tooltip', 'title' => 'Afficher les enfants'))
  492. ->setTemplatePath('@LcSov/adminlte/crud/action/index_children.html.twig')
  493. ->setCssClass('btn btn-sm btn-success');
  494. $backParentAction = Action::new(
  495. 'index_parent',
  496. $this->get('translator_admin')->transAction('index_parent'),
  497. 'fa fa-chevron-left'
  498. )
  499. ->linkToCrudAction(Action::INDEX)
  500. ->setCssClass('btn btn-sm btn-info')
  501. ->createAsGlobalAction();
  502. $actions->add(Crud::PAGE_INDEX, $backParentAction);
  503. $actions->add(Crud::PAGE_INDEX, $indexChildAction);
  504. }
  505. }
  506. public function actionUpdate($actions, $crudActionName, $actionName, array $button): void
  507. {
  508. if ($actions->getAsDto('actions')->getAction($crudActionName, $actionName)) {
  509. $actions->update(
  510. $crudActionName,
  511. $actionName,
  512. function (Action $action) use ($button) {
  513. if (isset($button['add_class'])) {
  514. $action->addCssClass($button['add_class']);
  515. }
  516. if (isset($button['class'])) {
  517. $action->setCssClass($button['class']);
  518. }
  519. if (isset($button['icon'])) {
  520. $action->setIcon('fa fa-'.$button['icon']);
  521. }
  522. if (isset($button['label'])) {
  523. $action->setLabel($button['label']);
  524. }
  525. if (isset($button['dropdown']) && $button['dropdown']) {
  526. $action->addCssClass('in-dropdown');
  527. }
  528. if (isset($button['html_attributes']) && $button['html_attributes']) {
  529. $action->setHtmlAttributes($button['html_attributes']);
  530. }
  531. return $action;
  532. }
  533. );
  534. }
  535. }
  536. }