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.

477 lines
19KB

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