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.

451 lines
17KB

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