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.

460 line
18KB

  1. <?php
  2. namespace Lc\SovBundle\Controller;
  3. use Doctrine\ORM\EntityManagerInterface;
  4. use Doctrine\ORM\QueryBuilder;
  5. use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
  6. use EasyCorp\Bundle\EasyAdminBundle\Collection\FilterCollection;
  7. use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
  8. use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
  9. use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
  10. use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
  11. use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
  12. use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController as EaAbstractCrudController;
  13. use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
  14. use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
  15. use EasyCorp\Bundle\EasyAdminBundle\Event\AfterCrudActionEvent;
  16. use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityUpdatedEvent;
  17. use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeCrudActionEvent;
  18. use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityDeletedEvent;
  19. use EasyCorp\Bundle\EasyAdminBundle\Exception\ForbiddenActionException;
  20. use EasyCorp\Bundle\EasyAdminBundle\Exception\InsufficientEntityPermissionException;
  21. use EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory;
  22. use EasyCorp\Bundle\EasyAdminBundle\Factory\FilterFactory;
  23. use EasyCorp\Bundle\EasyAdminBundle\Factory\PaginatorFactory;
  24. use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;
  25. use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
  26. use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
  27. use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
  28. use Lc\SovBundle\Doctrine\Extension\DevAliasInterface;
  29. use Lc\SovBundle\Doctrine\Extension\SeoInterface;
  30. use Lc\SovBundle\Doctrine\Extension\SortableInterface;
  31. use Lc\SovBundle\Doctrine\Extension\TranslatableInterface;
  32. use Lc\SovBundle\Doctrine\Extension\TreeInterface;
  33. use Lc\SovBundle\Field\CollectionField;
  34. use Lc\SovBundle\Form\Common\PositionType;
  35. use Lc\SovBundle\Translation\TranslatorAdmin;
  36. use Symfony\Component\Form\Extension\Core\Type\CollectionType;
  37. use Symfony\Component\Form\Extension\Core\Type\TextType;
  38. use Symfony\Component\HttpFoundation\RequestStack;
  39. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  40. abstract class AbstractAdminController extends EaAbstractCrudController
  41. {
  42. public static function getSubscribedServices()
  43. {
  44. return array_merge(parent::getSubscribedServices(), [
  45. 'session' => SessionInterface::class,
  46. 'request' => RequestStack::class,
  47. 'em' => EntityManagerInterface::class,
  48. 'translator_admin' => TranslatorAdmin::class,
  49. ]);
  50. }
  51. public function configureActions(Actions $actions): Actions
  52. {
  53. /* Translatable */
  54. if ($this->isInstanceOf(TranslatableInterface::class)) {
  55. $actions->update(
  56. Crud::PAGE_INDEX,
  57. Action::EDIT,
  58. function (Action $action) {
  59. $action->setTemplatePath('@LcSov/adminlte/crud/action/translatable.html.twig');
  60. return $action;
  61. }
  62. );
  63. }
  64. /* Boutons des actions dans les listes */
  65. $actionsArray[Crud::PAGE_INDEX] = [
  66. Action::NEW => [
  67. 'icon' => 'plus',
  68. 'label' => $this->get('translator_admin')->transAction('create'),
  69. 'add_class' => 'btn-sm'
  70. ],
  71. Action::EDIT => [
  72. 'class' => 'btn btn-sm btn-primary',
  73. 'icon' => 'edit',
  74. 'label' => false,
  75. 'html_attributes' => array(
  76. 'data-toggle' => 'tooltip',
  77. 'title' => $this->get('translator_admin')->transAction('edit')
  78. )
  79. ],
  80. Action::DETAIL => [
  81. 'icon' => 'eye',
  82. 'add_class' => 'btn btn-sm btn-success',
  83. 'label' => false,
  84. 'html_attributes' => array(
  85. 'data-toggle' => 'tooltip',
  86. 'title' => $this->get('translator_admin')->transAction('detail')
  87. )
  88. ],
  89. Action::DELETE => [
  90. 'icon' => 'trash',
  91. 'dropdown' => true,
  92. 'label' => $this->get('translator_admin')->transAction('delete')
  93. ],
  94. Action::BATCH_DELETE => [
  95. 'class' => 'btn btn-sm btn-danger',
  96. 'icon' => 'trash',
  97. 'label' => $this->get('translator_admin')->transAction('delete')
  98. ],
  99. ];
  100. /* Boutons des actions dans l'édition */
  101. $actionSaveAndReturn = [
  102. 'add_class' => 'float-right',
  103. 'icon' => 'check',
  104. 'label' => $this->get('translator_admin')->transAction('save_and_return')
  105. ];
  106. $actionIndex = [
  107. 'icon' => 'chevron-left',
  108. 'class' => 'btn btn-link',
  109. 'label' => $this->get('translator_admin')->transAction('back_index')
  110. ];
  111. $actionsArray[Crud::PAGE_EDIT] = [
  112. Action::SAVE_AND_CONTINUE => [
  113. 'class' => 'btn btn-info float-right',
  114. 'label' => $this->get('translator_admin')->transAction('save_and_continue')
  115. ],
  116. Action::DELETE => [
  117. 'icon' => 'trash',
  118. 'class' => 'btn btn-outline-danger action-delete',
  119. 'label' => $this->get('translator_admin')->transAction('delete')
  120. ],
  121. Action::SAVE_AND_RETURN => $actionSaveAndReturn,
  122. Action::INDEX => $actionIndex,
  123. ];
  124. $actionsArray[Crud::PAGE_NEW] = [
  125. Action::SAVE_AND_ADD_ANOTHER => [
  126. 'class' => 'btn btn-info float-right',
  127. 'label' => $this->get('translator_admin')->transAction('save_and_add_another')
  128. ],
  129. Action::SAVE_AND_RETURN => $actionSaveAndReturn,
  130. Action::INDEX => $actionIndex,
  131. ];
  132. $actions->add(Crud::PAGE_EDIT, Action::INDEX);
  133. $actions->add(Crud::PAGE_EDIT, Action::DELETE);
  134. $actions->add(Crud::PAGE_NEW, Action::INDEX);
  135. if ($this->isInstanceOf(SortableInterface::class)) {
  136. $sortAction = Action::new('sort', $this->get('translator_admin')->transAction('sort'), 'fa fa-sort')
  137. ->linkToCrudAction('sort')
  138. ->setCssClass('btn btn-sm btn-success')
  139. ->createAsGlobalAction();
  140. $actions->add(Crud::PAGE_INDEX, $sortAction);
  141. }
  142. if ($this->isInstanceOf(TreeInterface::class)) {
  143. $indexChildAction = Action::new(
  144. 'index_children',
  145. $this->get('translator_admin')->transAction('index_children'),
  146. 'fa fa-list'
  147. )
  148. ->linkToCrudAction(Action::INDEX)
  149. ->setLabel('')
  150. ->setHtmlAttributes(array('data-toggle' => 'tooltip', 'title' => 'Afficher les enfants'))
  151. ->setTemplatePath('@LcSov/adminlte/crud/action/index_children.html.twig')
  152. ->setCssClass('btn btn-sm btn-success');
  153. $backParentAction = Action::new(
  154. 'index_parent',
  155. $this->get('translator_admin')->transAction('index_parent'),
  156. 'fa fa-chevron-left'
  157. )
  158. ->linkToCrudAction(Action::INDEX)
  159. ->setCssClass('btn btn-sm btn-info')
  160. ->createAsGlobalAction();
  161. $actions->add(Crud::PAGE_INDEX, $backParentAction);
  162. $actions->add(Crud::PAGE_INDEX, $indexChildAction);
  163. }
  164. $actions->reorder(Crud::PAGE_EDIT, [Action::INDEX, Action::SAVE_AND_RETURN, Action::SAVE_AND_CONTINUE]);
  165. $actions->reorder(Crud::PAGE_NEW, [Action::INDEX, Action::SAVE_AND_RETURN, Action::SAVE_AND_ADD_ANOTHER]);
  166. foreach ($actionsArray as $crudActionName => $actionsStyle) {
  167. foreach ($actionsStyle as $actionName => $button) {
  168. // si l'action existe
  169. if ($actions->getAsDto('actions')->getAction($crudActionName, $actionName)) {
  170. $actions->update(
  171. $crudActionName,
  172. $actionName,
  173. function (Action $action) use ($button) {
  174. if (isset($button['add_class'])) {
  175. $action->addCssClass($button['add_class']);
  176. }
  177. if (isset($button['class'])) {
  178. $action->setCssClass($button['class']);
  179. }
  180. if (isset($button['icon'])) {
  181. $action->setIcon('fa fa-' . $button['icon']);
  182. }
  183. if (isset($button['label'])) {
  184. $action->setLabel($button['label']);
  185. }
  186. if (isset($button['dropdown']) && $button['dropdown']) {
  187. $action->addCssClass('in-dropdown');
  188. }
  189. if (isset($button['html_attributes']) && $button['html_attributes']) {
  190. $action->setHtmlAttributes($button['html_attributes']);
  191. }
  192. return $action;
  193. }
  194. );
  195. }
  196. }
  197. }
  198. return $actions;
  199. }
  200. public function configureResponseParameters(KeyValueStore $responseParameters): KeyValueStore
  201. {
  202. if (Crud::PAGE_INDEX === $responseParameters->get('pageName')) {
  203. $responseParameters->set('fields', $this->configureFields('index'));
  204. }
  205. return $responseParameters;
  206. }
  207. public function configureCrud(Crud $crud): Crud
  208. {
  209. $crud = parent::configureCrud($crud);
  210. $this->setMaxResults($crud);
  211. if ($this->isInstanceOf(SortableInterface::class)) {
  212. $crud->setDefaultSort(['position' => 'ASC']);
  213. }
  214. return $crud;
  215. }
  216. public function setMaxResults(Crud $crud)
  217. {
  218. $entityClass = $this->getEntityFqcn();
  219. $paramListMaxResults = 'listMaxResults';
  220. $paramSessionListMaxResults = $entityClass . '-' . $paramListMaxResults;
  221. $requestListMaxResults = $this->get('request')->getCurrentRequest()->get($paramListMaxResults);
  222. if ($requestListMaxResults) {
  223. $this->get('session')->set($paramSessionListMaxResults, $requestListMaxResults);
  224. }
  225. $maxResults = $this->get('session')->get($paramSessionListMaxResults) ? $this->get('session')->get(
  226. $paramSessionListMaxResults
  227. ) : 30;
  228. $crud->setPaginatorPageSize($maxResults);
  229. }
  230. public function configureFields(string $pageName): iterable
  231. {
  232. $seoPanel = $confPanel = array();
  233. if ($this->isInstanceOf(SeoInterface::class)) {
  234. $seoPanel = [
  235. FormField::addPanel('seo')->setTemplateName('crud/field/generic'),
  236. TextField::new('metaTitle')->setLabel('Meta Title')->setHelp(
  237. 'Affiché dans les résultats de recherche Google'
  238. )->hideOnIndex(),
  239. TextareaField::new('metaDescription')->setLabel('Meta description')->setHelp(
  240. 'Affiché dans les résultats de recherche Google'
  241. )->hideOnIndex(),
  242. CollectionField::new('oldUrls')
  243. ->setFormTypeOption('entry_type', TextType::class)->setLabel(
  244. 'Anciennes urls du document'
  245. )->hideOnIndex(),
  246. ];
  247. }
  248. if ($this->isInstanceOf(DevAliasInterface::class)) {
  249. $confPanel = [
  250. FormField::addPanel('configuration')->setTemplateName('crud/field/generic'),
  251. TextField::new('devAlias')->hideOnIndex(),
  252. ];
  253. }
  254. return array_merge($seoPanel, $confPanel);
  255. }
  256. public function sort(AdminContext $context)
  257. {
  258. $event = new BeforeCrudActionEvent($context);
  259. $this->get('event_dispatcher')->dispatch($event);
  260. if ($event->isPropagationStopped()) {
  261. return $event->getResponse();
  262. }
  263. //if (!$this->isGranted(Permission::EA_EXECUTE_ACTION) || !$this->isInstanceOf(SortableInterface::class)) {
  264. if (!$this->isInstanceOf(SortableInterface::class)) {
  265. throw new ForbiddenActionException($context);
  266. }
  267. $fields = FieldCollection::new($this->configureFields(Crud::PAGE_INDEX));
  268. $filters = $this->get(FilterFactory::class)->create(
  269. $context->getCrud()->getFiltersConfig(),
  270. $fields,
  271. $context->getEntity()
  272. );
  273. $queryBuilder = $this->createIndexQueryBuilder($context->getSearch(), $context->getEntity(), $fields, $filters);
  274. $paginator = $this->get(PaginatorFactory::class)->create($queryBuilder);
  275. $entities = $this->get(EntityFactory::class)->createCollection($context->getEntity(), $paginator->getResults());
  276. $this->get(EntityFactory::class)->processFieldsForAll($entities, $fields);
  277. $sortableForm = $this->createFormBuilder(array('entities', $paginator->getResults()))
  278. ->add(
  279. 'entities',
  280. CollectionType::class,
  281. array(
  282. 'required' => true,
  283. 'allow_add' => true,
  284. 'entry_type' => PositionType::class,
  285. )
  286. )
  287. ->getForm();
  288. $entityManager = $this->getDoctrine()->getManagerForClass($this->getEntityFqcn());
  289. $repository = $entityManager->getRepository($this->getEntityFqcn());
  290. $sortableForm->handleRequest($context->getRequest());
  291. if ($sortableForm->isSubmitted() && $sortableForm->isValid()) {
  292. foreach ($sortableForm->get('entities')->getData() as $elm) {
  293. $entityInstance = $repository->find($elm['id']);
  294. $entityDto = $context->getEntity()->newWithInstance($entityInstance);
  295. if (!$entityDto->isAccessible()) {
  296. throw new InsufficientEntityPermissionException($context);
  297. }
  298. $event = new BeforeEntityDeletedEvent($entityInstance);
  299. $this->get('event_dispatcher')->dispatch($event);
  300. $entityInstance = $event->getEntityInstance();
  301. $entityInstance->setPosition($elm['position']);
  302. $this->updateEntity($entityManager, $entityInstance);
  303. $this->get('event_dispatcher')->dispatch(new AfterEntityUpdatedEvent($entityInstance));
  304. }
  305. $url = $this->get(AdminUrlGenerator::class)
  306. ->setAction(Action::INDEX)
  307. ->generateUrl();
  308. $this->addFlash('success', $this->translatorAdmin->transFlashMessage('sort'), array());
  309. return $this->redirect($url);
  310. }
  311. $responseParameters = $this->configureResponseParameters(
  312. KeyValueStore::new(
  313. [
  314. 'pageName' => Crud::PAGE_INDEX,
  315. 'templatePath' => '@LcSov/adminlte/crud/sort.html.twig',
  316. 'entities' => $entities,
  317. 'paginator' => $paginator,
  318. 'global_actions' => array(),
  319. 'batch_actions' => array(),
  320. 'filters' => $filters,
  321. 'sortable_form' => $sortableForm,
  322. ]
  323. )
  324. );
  325. $responseParameters->set('fields', $this->configureFields('index'));
  326. $event = new AfterCrudActionEvent($context, $responseParameters);
  327. $this->get('event_dispatcher')->dispatch($event);
  328. if ($event->isPropagationStopped()) {
  329. return $event->getResponse();
  330. }
  331. return $responseParameters;
  332. }
  333. public function createIndexQueryBuilder(
  334. SearchDto $searchDto,
  335. EntityDto $entityDto,
  336. FieldCollection $fields,
  337. FilterCollection $filters
  338. ): QueryBuilder {
  339. $queryBuilder = parent::createIndexQueryBuilder(
  340. $searchDto,
  341. $entityDto,
  342. $fields,
  343. $filters
  344. );
  345. if ($this->isInstanceOf(TreeInterface::class)) {
  346. $entityId = $searchDto->getRequest()->get('entityId');
  347. if ($entityId !== null) {
  348. $queryBuilder->andWhere('entity.parent = :entityId');
  349. $queryBuilder->setParameter('entityId', $searchDto->getRequest()->get('entityId'));
  350. } else {
  351. $queryBuilder->andWhere('entity.parent IS NULL');
  352. }
  353. }
  354. return $queryBuilder;
  355. }
  356. public function createSortQueryBuilder(
  357. SearchDto $searchDto,
  358. EntityDto $entityDto,
  359. FieldCollection $fields,
  360. FilterCollection $filters
  361. ): QueryBuilder {
  362. $queryBuilder = $this->createIndexQueryBuilder($searchDto, $entityDto, $fields, $filters);
  363. return $queryBuilder;
  364. }
  365. public function edit(AdminContext $context)
  366. {
  367. $response = parent::edit($context);;
  368. // on vide le flash bag si édition en ajax (notification déjà affichée en Javascript)
  369. if ($context->getRequest()->isXmlHttpRequest()) {
  370. $this->get('session')->getFlashBag()->clear();
  371. }
  372. return $response;
  373. }
  374. public function isInstanceOf(string $interfaceName): bool
  375. {
  376. return in_array($interfaceName, class_implements($this->getEntityFqcn()));
  377. }
  378. public function updateEntity(EntityManagerInterface $entityManager, $entityInstance): void
  379. {
  380. $entityManager->update($entityInstance);
  381. $entityManager->flush();
  382. }
  383. public function persistEntity(EntityManagerInterface $entityManager, $entityInstance): void
  384. {
  385. $entityManager->create($entityInstance);
  386. $entityManager->flush();
  387. }
  388. }