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.

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