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.

878 lines
32KB

  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\Config\Option\EA;
  14. use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
  15. use EasyCorp\Bundle\EasyAdminBundle\Contracts\Controller\CrudControllerInterface;
  16. use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController as EaAbstractCrudController;
  17. use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
  18. use EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto;
  19. use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
  20. use EasyCorp\Bundle\EasyAdminBundle\Event\AfterCrudActionEvent;
  21. use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeCrudActionEvent;
  22. use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityUpdatedEvent;
  23. use EasyCorp\Bundle\EasyAdminBundle\Exception\ForbiddenActionException;
  24. use EasyCorp\Bundle\EasyAdminBundle\Exception\InsufficientEntityPermissionException;
  25. use EasyCorp\Bundle\EasyAdminBundle\Factory\ControllerFactory;
  26. use EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory;
  27. use EasyCorp\Bundle\EasyAdminBundle\Factory\FilterFactory;
  28. use EasyCorp\Bundle\EasyAdminBundle\Factory\PaginatorFactory;
  29. use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
  30. use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;
  31. use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
  32. use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
  33. use EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider;
  34. use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
  35. use EasyCorp\Bundle\EasyAdminBundle\Security\Permission;
  36. use Lc\SovBundle\Component\EntityComponent;
  37. use Lc\SovBundle\Definition\ActionDefinition;
  38. use Lc\SovBundle\Doctrine\Extension\DevAliasInterface;
  39. use Lc\SovBundle\Doctrine\Extension\SeoInterface;
  40. use Lc\SovBundle\Doctrine\Extension\SortableInterface;
  41. use Lc\SovBundle\Doctrine\Extension\StatusInterface;
  42. use Lc\SovBundle\Doctrine\Extension\TranslatableInterface;
  43. use Lc\SovBundle\Doctrine\Extension\TreeInterface;
  44. use Lc\SovBundle\Field\CollectionField;
  45. use Lc\SovBundle\Form\Common\FiltersFormType;
  46. use Lc\SovBundle\Form\Common\PositionType;
  47. use Lc\SovBundle\Repository\EntityRepository;
  48. use Lc\SovBundle\Repository\RepositoryQueryInterface;
  49. use Lc\SovBundle\Translation\TranslatorAdmin;
  50. use Symfony\Component\Form\Extension\Core\Type\CollectionType;
  51. use Symfony\Component\Form\Extension\Core\Type\TextType;
  52. use Symfony\Component\Form\FormInterface;
  53. use Symfony\Component\HttpFoundation\JsonResponse;
  54. use Symfony\Component\HttpFoundation\Response;
  55. abstract class AbstractAdminController extends EaAbstractCrudController
  56. {
  57. use ControllerTrait;
  58. protected FormInterface $filtersForm;
  59. protected TranslatorAdmin $translatorAdmin;
  60. protected bool $isRepositoryQueryFiltered = false;
  61. abstract public function getRepositoryQuery(): RepositoryQueryInterface;
  62. public function configureResponseParameters(KeyValueStore $responseParameters): KeyValueStore
  63. {
  64. if ($responseParameters->get('global_actions')) {
  65. $this->overrideGlobalActions($responseParameters->get('global_actions'));
  66. }
  67. if ($responseParameters->get('entities')) {
  68. $this->overrideEntitiesActions($responseParameters->get('entities'), $responseParameters->get('pageName'));
  69. }
  70. if (Crud::PAGE_INDEX === $responseParameters->get('pageName')) {
  71. $responseParameters->set('fields', $this->configureFields('index'));
  72. if (isset($this->filtersForm)) {
  73. $responseParameters->set('filters_form', $this->filtersForm);
  74. }
  75. }
  76. $responseParameters->set('translation_entity_name', $this->getTranslationEntityName());
  77. return $responseParameters;
  78. }
  79. public function getTranslationEntityName()
  80. {
  81. return $this->getEntityFqcn();
  82. }
  83. public function overrideEntitiesActions(?EntityCollection $entities, string $pageName): void
  84. {
  85. }
  86. public function overrideGlobalActions(?ActionCollection $actions): void
  87. {
  88. if ($actions) {
  89. $context = $this->getAdminContextProvider()->getContext();
  90. $adminUrlGenerator = $this->getAdminUrlGenerator();
  91. foreach ($actions as $i => $action) {
  92. //récriture du bouton 'retour au parent'
  93. if ($action->getName() == 'index_parent') {
  94. $entity = $context->getEntity()->getInstance();
  95. if ($entity !== null) {
  96. if ($entity->getParent() !== null) {
  97. $url = $adminUrlGenerator
  98. ->setController($context->getCrud()->getControllerFqcn())
  99. ->set('entityId', $entity->getParent()->getId())
  100. ->generateUrl();
  101. $action->setLinkUrl($url);
  102. }
  103. } else {
  104. unset($actions[$i]);
  105. }
  106. }
  107. if ($action->getName() == 'sort') {
  108. $entityId = $context->getRequest()->get('entityId');
  109. if ($entityId != null) {
  110. $url = $adminUrlGenerator
  111. ->setController($context->getCrud()->getControllerFqcn())
  112. ->setAction($action->getName())
  113. ->set('entityId', $entityId)
  114. ->generateUrl();
  115. $action->setLinkUrl($url);
  116. }
  117. }
  118. if ($action->getName() == ActionDefinition::WRITE_TO_USER) {
  119. $entity = $context->getEntity()->getInstance();
  120. if ($entity !== null) {
  121. if (method_exists($entity, 'getUser')) {
  122. $url = $this->generateEaUrl(UserA)->setController($context->getCrud()->getControllerFqcn())
  123. ->set('entityId', $entity->getParent()->getId())
  124. ->generateUrl();
  125. $action->setLinkUrl($url);
  126. }
  127. } else {
  128. unset($actions[$i]);
  129. }
  130. }
  131. }
  132. }
  133. }
  134. protected function getRequestCrudAction(): string
  135. {
  136. return $this->getRequestStack()->getCurrentRequest()->get('crudAction');
  137. }
  138. public function configureCrud(Crud $crud): Crud
  139. {
  140. $crud = parent::configureCrud($crud);
  141. if ($this->getRequestCrudAction() === ActionDefinition::SORT) {
  142. $crud->setPaginatorPageSize(9999);
  143. } else {
  144. $this->setMaxResults($crud);
  145. }
  146. $crud->setFormOptions(['translation_entity_name' => $this->getTranslationEntityName()]);
  147. if ($this->isInstanceOf(SortableInterface::class)) {
  148. $crud->setDefaultSort(['position' => 'ASC']);
  149. } else {
  150. $crud->setDefaultSort(['id' => 'DESC']);
  151. }
  152. return $crud;
  153. }
  154. public function setMaxResults(Crud $crud): void
  155. {
  156. $entityClass = $this->getEntityFqcn();
  157. $paramListMaxResults = 'listMaxResults';
  158. $paramSessionListMaxResults = $entityClass . '-' . $paramListMaxResults;
  159. $requestListMaxResults = $this->getRequestStack()->getCurrentRequest()->get($paramListMaxResults);
  160. if ($requestListMaxResults) {
  161. $this->getRequestStack()->getSession()->set($paramSessionListMaxResults, $requestListMaxResults);
  162. }
  163. $maxResults = $this->getRequestStack()->getSession()->get(
  164. $paramSessionListMaxResults
  165. ) ? $this->requestStack->getSession()->get($paramSessionListMaxResults) : 30;
  166. // $maxResults = $this->getRequestStack()->getSession()->get($paramSessionListMaxResults) ? $this->get(
  167. // 'session'
  168. // )->get(
  169. // $paramSessionListMaxResults
  170. // ) : 30;
  171. $crud->setPaginatorPageSize($maxResults);
  172. }
  173. public function getSeoPanel(): ?array
  174. {
  175. if ($this->isInstanceOf(SeoInterface::class)) {
  176. return [
  177. FormField::addPanel('seo')->setTemplateName('crud/field/generic'),
  178. TextField::new('metaTitle')->setLabel('Meta Title')->setHelp(
  179. 'Affiché dans les résultats de recherche Google'
  180. )->hideOnIndex(),
  181. TextareaField::new('metaDescription')->setLabel('Meta description')->setHelp(
  182. 'Affiché dans les résultats de recherche Google'
  183. )->hideOnIndex(),
  184. CollectionField::new('oldUrls')
  185. ->setFormTypeOption('entry_type', TextType::class)->setLabel(
  186. 'Anciennes urls du document'
  187. )->hideOnIndex(),
  188. ];
  189. } else {
  190. return null;
  191. }
  192. }
  193. public function getConfPanel(): ?array
  194. {
  195. if ($this->isInstanceOf(DevAliasInterface::class)) {
  196. return [
  197. FormField::addPanel('configuration')->setTemplateName('crud/field/generic'),
  198. TextField::new('devAlias')->hideOnIndex(),
  199. ];
  200. } else {
  201. return null;
  202. }
  203. }
  204. public function sort(AdminContext $context)
  205. {
  206. $event = new BeforeCrudActionEvent($context);
  207. $this->container->get('event_dispatcher')->dispatch($event);
  208. if ($event->isPropagationStopped()) {
  209. return $event->getResponse();
  210. }
  211. //if (!$this->isGranted(Permission::EA_EXECUTE_ACTION) || !$this->isInstanceOf(SortableInterface::class)) {
  212. if (!$this->isInstanceOf(SortableInterface::class)) {
  213. throw new ForbiddenActionException($context);
  214. }
  215. $fields = FieldCollection::new($this->configureFields(Crud::PAGE_INDEX));
  216. $filters = $this->container->get(FilterFactory::class)->create(
  217. $context->getCrud()->getFiltersConfig(),
  218. $fields,
  219. $context->getEntity()
  220. );
  221. $queryBuilder = $this->createSortQueryBuilder($context->getSearch(), $context->getEntity(), $fields, $filters);
  222. $paginator = $this->container->get(PaginatorFactory::class)->create($queryBuilder);
  223. $entities = $this->container->get(EntityFactory::class)->createCollection(
  224. $context->getEntity(),
  225. $paginator->getResults()
  226. );
  227. $this->container->get(EntityFactory::class)->processFieldsForAll($entities, $fields);
  228. $sortableForm = $this->createFormBuilder(array('entities', $paginator->getResults()))
  229. ->add(
  230. 'entities',
  231. CollectionType::class,
  232. array(
  233. 'required' => true,
  234. 'allow_add' => true,
  235. 'entry_type' => PositionType::class,
  236. )
  237. )
  238. ->getForm();
  239. $entityManager = $this->container->get('doctrine')->getManagerForClass($this->getEntityFqcn());
  240. $repository = $entityManager->getRepository($this->getEntityFqcn());
  241. $sortableForm->handleRequest($context->getRequest());
  242. if ($sortableForm->isSubmitted() && $sortableForm->isValid()) {
  243. foreach ($sortableForm->get('entities')->getData() as $elm) {
  244. $entityInstance = $repository->find($elm['id']);
  245. $entityDto = $context->getEntity()->newWithInstance($entityInstance);
  246. if (!$entityDto->isAccessible()) {
  247. throw new InsufficientEntityPermissionException($context);
  248. }
  249. $event = new BeforeEntityUpdatedEvent($entityInstance);
  250. $this->container->get('event_dispatcher')->dispatch($event);
  251. $entityInstance = $event->getEntityInstance();
  252. $entityInstance->setPosition($elm['position']);
  253. $this->updateEntity($entityManager, $entityInstance);
  254. // @TODO : ce dispatch déclenche UpdateProductfamilyAfterFlushEventSubscriber dans Caracole et on tombe sur une erreur "Maximum execution time"
  255. //$this->get('event_dispatcher')->dispatch(new AfterEntityUpdatedEvent($entityInstance));
  256. }
  257. $url = $this->container->get(AdminUrlGenerator::class)
  258. ->setAction(ActionDefinition::INDEX)
  259. ->generateUrl();
  260. $this->addFlashTranslator('success', 'sorted');
  261. return $this->redirect($url);
  262. }
  263. $responseParameters = $this->configureResponseParameters(
  264. KeyValueStore::new(
  265. [
  266. 'pageName' => Crud::PAGE_INDEX,
  267. 'templatePath' => '@LcSov/adminlte/crud/sort.html.twig',
  268. 'entities' => $entities,
  269. 'paginator' => $paginator,
  270. 'global_actions' => array(),
  271. 'batch_actions' => array(),
  272. 'filters' => $filters,
  273. 'sortable_form' => $sortableForm,
  274. ]
  275. )
  276. );
  277. $responseParameters->set('fields', $this->configureFields('index'));
  278. $event = new AfterCrudActionEvent($context, $responseParameters);
  279. $this->container->get('event_dispatcher')->dispatch($event);
  280. if ($event->isPropagationStopped()) {
  281. return $event->getResponse();
  282. }
  283. return $responseParameters;
  284. }
  285. public function duplicate(
  286. AdminContext $context,
  287. EntityComponent $entityComponent,
  288. TranslatorAdmin $translatorAdmin,
  289. EntityManagerInterface $em
  290. ) {
  291. if (!$this->isGranted(
  292. Permission::EA_EXECUTE_ACTION,
  293. ['action' => ActionDefinition::DUPLICATE, 'entity' => $context->getEntity()]
  294. )) {
  295. throw new ForbiddenActionException($context);
  296. }
  297. if (!$context->getEntity()->isAccessible()) {
  298. throw new InsufficientEntityPermissionException($context);
  299. }
  300. $newEntity = $entityComponent->duplicateEntity($context->getEntity()->getInstance());
  301. $em->create($newEntity, false);
  302. $em->flush();
  303. $url = $this->get(AdminUrlGenerator::class)
  304. ->setAction(ActionDefinition::EDIT)
  305. ->setEntityId($newEntity->getId())
  306. ->generateUrl();
  307. $this->addFlashTranslator('success', ActionDefinition::DUPLICATE);
  308. return $this->redirect($url);
  309. }
  310. public function isRepositoryQueryFiltered(): bool
  311. {
  312. if (($this->filtersForm && $this->filtersForm->isSubmitted()) || $this->isRepositoryQueryFiltered) {
  313. return true;
  314. } else {
  315. return false;
  316. }
  317. }
  318. public function createIndexRepositoryQuery(
  319. SearchDto $searchDto,
  320. EntityDto $entityDto,
  321. FieldCollection $fields,
  322. FilterCollection $filters
  323. ): RepositoryQueryInterface {
  324. $repositoryQuery = $this->container->get(EntityRepository::class)->createRepositoryQuery(
  325. $this->getRepositoryQuery(),
  326. $searchDto,
  327. $entityDto,
  328. $fields,
  329. $filters
  330. );
  331. // TODO : déplacer dans EntotyRepository
  332. if ($this->isInstanceOf(TreeInterface::class)) {
  333. if ($entityDto->getInstance()) {
  334. $repositoryQuery->filterByParent($entityDto->getInstance());
  335. } else {
  336. $repositoryQuery->filterIsParent();
  337. }
  338. }
  339. if ($this->isInstanceOf(StatusInterface::class)) {
  340. $repositoryQuery->filterIsOnlineAndOffline();
  341. }
  342. $this->filtersForm = $this->createForm(
  343. FiltersFormType::class,
  344. null,
  345. array(
  346. 'fields' => $fields,
  347. 'entity_dto' => $entityDto,
  348. 'entity_class' => $this->getEntityFqcn(),
  349. 'entity_name' => $entityDto->getName(),
  350. )
  351. );
  352. $filterManager = $this->getFilterManager();
  353. $this->filtersForm->handleRequest($searchDto->getRequest());
  354. $this->isRepositoryQueryFiltered = $filterManager->handleFiltersForm(
  355. $repositoryQuery,
  356. $this->filtersForm,
  357. $fields,
  358. $entityDto
  359. );
  360. return $repositoryQuery;
  361. }
  362. public function createSortRepositoryQuery(
  363. SearchDto $searchDto,
  364. EntityDto $entityDto,
  365. FieldCollection $fields,
  366. FilterCollection $filters
  367. ): RepositoryQueryInterface {
  368. $repositoryQuery = $this->createIndexRepositoryQuery($searchDto, $entityDto, $fields, $filters);
  369. if ($this->isInstanceOf(StatusInterface::class)) {
  370. $repositoryQuery->filterIsOnline();
  371. }
  372. $repositoryQuery->orderBy('position', 'asc');
  373. return $repositoryQuery;
  374. }
  375. public function createIndexQueryBuilder(
  376. SearchDto $searchDto,
  377. EntityDto $entityDto,
  378. FieldCollection $fields,
  379. FilterCollection $filters
  380. ): QueryBuilder {
  381. $repositoryQuery = $this->createIndexRepositoryQuery($searchDto, $entityDto, $fields, $filters);
  382. return $repositoryQuery->getQueryBuilder();
  383. }
  384. public function createSortQueryBuilder(
  385. SearchDto $searchDto,
  386. EntityDto $entityDto,
  387. FieldCollection $fields,
  388. FilterCollection $filters
  389. ): QueryBuilder {
  390. $repositoryQuery = $this->createSortRepositoryQuery($searchDto, $entityDto, $fields, $filters);
  391. return $repositoryQuery->getQueryBuilder();
  392. }
  393. public function edit(AdminContext $context)
  394. {
  395. //Pour l'edit du status dans le listing
  396. if($context->getRequest()->isXmlHttpRequest()){
  397. $context->getRequest()->setMethod('PATCH');
  398. }
  399. $response = parent::edit($context);
  400. // on vide le flash bag si édition en ajax (notification déjà affichée en Javascript)
  401. if ($context->getRequest()->isXmlHttpRequest()) {
  402. $this->requestStack->getSession()->getFlashBag()->clear();
  403. }
  404. return $response;
  405. }
  406. public function getControllerFqcnByInterface(string $interface): string
  407. {
  408. $context = $this->get(AdminContextProvider::class)->getContext();
  409. return $context->getCrudControllers()->findCrudFqcnByEntityFqcn(
  410. $this->get(EntityManagerInterface::class)->getEntityName($interface)
  411. );
  412. }
  413. public function updateEntity(EntityManagerInterface $entityManager, $entityInstance): void
  414. {
  415. $entityManager->update($entityInstance);
  416. $entityManager->flush();
  417. $this->getFlashBagTranslator()->add('success', 'updated', $this->getTranslationEntityName());
  418. }
  419. public function persistEntity(EntityManagerInterface $entityManager, $entityInstance): void
  420. {
  421. $entityManager->create($entityInstance);
  422. $entityManager->flush();
  423. $this->getFlashBagTranslator()->add('success', 'created', $this->getTranslationEntityName());
  424. }
  425. public function deleteEntity(EntityManagerInterface $entityManager, $entityInstance): void
  426. {
  427. if ($this->isInstanceOf(StatusInterface::class)) {
  428. $entityInstance->setStatus(-1);
  429. $entityManager->update($entityInstance);
  430. } else {
  431. $entityManager->delete($entityInstance);
  432. }
  433. $entityManager->flush();
  434. $this->getFlashBagTranslator()->add('success', 'deleted', $this->getTranslationEntityName());
  435. }
  436. public function configureActions(Actions $actions): Actions
  437. {
  438. $this->buildIndexActions($actions);
  439. $this->buildEditActions($actions);
  440. $this->buildDetailActions($actions);
  441. $this->buildNewActions($actions);
  442. $this->handleTranslatableEntityActions($actions);
  443. $this->handleSortableEntityActions($actions);
  444. $this->handleTreeEntityActions($actions);
  445. /*$actions->reorder(Crud::PAGE_EDIT, [ActionDefinition::INDEX, ActionDefinition::SAVE_AND_RETURN, ActionDefinition::SAVE_AND_CONTINUE, ActionDefinition::DELETE]);
  446. $actions->reorder(Crud::PAGE_NEW, [ActionDefinition::INDEX, ActionDefinition::SAVE_AND_RETURN, ActionDefinition::SAVE_AND_ADD_ANOTHER]);*/
  447. return $actions;
  448. }
  449. public function getDuplicateAction(): Action
  450. {
  451. $duplicateAction = Action::new(
  452. ActionDefinition::DUPLICATE,
  453. $this->translatorAdmin->transAction(ActionDefinition::DUPLICATE),
  454. 'fa fa-fw fa-copy'
  455. )
  456. ->linkToCrudAction(ActionDefinition::DUPLICATE)
  457. ->setLabel($this->translatorAdmin->transAction(ActionDefinition::DUPLICATE))
  458. ->setCssClass('in-dropdown text-info action-confirm');
  459. return $duplicateAction;
  460. }
  461. public function buildIndexActions(Actions $actions): void
  462. {
  463. $actions->add(Crud::PAGE_INDEX, $this->getDuplicateAction());
  464. $this->actionUpdate(
  465. $actions,
  466. Crud::PAGE_INDEX,
  467. ActionDefinition::NEW,
  468. [
  469. 'icon' => 'plus',
  470. 'label' => $this->translatorAdmin->transAction('create'),
  471. 'add_class' => 'btn-sm',
  472. ]
  473. );
  474. $this->actionUpdate(
  475. $actions,
  476. Crud::PAGE_INDEX,
  477. ActionDefinition::EDIT,
  478. [
  479. 'class' => 'btn btn-sm btn-primary',
  480. 'icon' => 'edit',
  481. 'label' => false,
  482. 'html_attributes' => array(
  483. 'data-toggle' => 'tooltip',
  484. 'title' => $this->translatorAdmin->transAction('edit'),
  485. ),
  486. ]
  487. );
  488. $this->actionUpdate(
  489. $actions,
  490. Crud::PAGE_INDEX,
  491. ActionDefinition::DETAIL,
  492. [
  493. 'icon' => 'eye',
  494. 'add_class' => 'btn btn-sm btn-success',
  495. 'label' => false,
  496. 'html_attributes' => array(
  497. 'data-toggle' => 'tooltip',
  498. 'title' => $this->translatorAdmin->transAction('detail'),
  499. ),
  500. ]
  501. );
  502. $this->actionUpdate(
  503. $actions,
  504. Crud::PAGE_INDEX,
  505. ActionDefinition::DELETE,
  506. [
  507. 'icon' => 'trash',
  508. 'dropdown' => true,
  509. 'label' => $this->translatorAdmin->transAction('delete'),
  510. 'class' => 'dropdown-item text-danger in-dropdown action-delete',
  511. ]
  512. );
  513. $this->actionUpdate(
  514. $actions,
  515. Crud::PAGE_INDEX,
  516. ActionDefinition::BATCH_DELETE,
  517. [
  518. 'class' => 'btn btn-sm btn-danger',
  519. 'icon' => 'trash',
  520. 'label' => $this->translatorAdmin->transAction('delete'),
  521. ]
  522. );
  523. }
  524. public function buildEditActions(Actions $actions): void
  525. {
  526. $actions->add(Crud::PAGE_EDIT, ActionDefinition::INDEX);
  527. $actions->add(Crud::PAGE_EDIT, ActionDefinition::DELETE);
  528. $this->actionUpdate(
  529. $actions,
  530. Crud::PAGE_EDIT,
  531. ActionDefinition::SAVE_AND_RETURN,
  532. [
  533. 'add_class' => 'float-right ',
  534. 'icon' => 'check',
  535. 'label' => $this->translatorAdmin->transAction('save_and_return'),
  536. ]
  537. );
  538. $this->actionUpdate(
  539. $actions,
  540. Crud::PAGE_EDIT,
  541. ActionDefinition::INDEX,
  542. [
  543. 'icon' => 'chevron-left',
  544. 'class' => 'btn btn-link',
  545. 'label' => $this->translatorAdmin->transAction('back_index'),
  546. ]
  547. );
  548. $this->actionUpdate(
  549. $actions,
  550. Crud::PAGE_EDIT,
  551. ActionDefinition::SAVE_AND_CONTINUE,
  552. [
  553. 'class' => 'btn btn-info float-right action-save-continue',
  554. 'label' => $this->translatorAdmin->transAction('save_and_continue'),
  555. ]
  556. );
  557. $this->actionUpdate(
  558. $actions,
  559. Crud::PAGE_EDIT,
  560. ActionDefinition::DELETE,
  561. [
  562. 'icon' => 'trash',
  563. 'class' => 'btn btn-outline-danger action-delete',
  564. 'label' => $this->translatorAdmin->transAction('delete'),
  565. ]
  566. );
  567. }
  568. public function buildDetailActions(Actions $actions): void
  569. {
  570. }
  571. public function buildNewActions(Actions $actions): void
  572. {
  573. $actions->add(Crud::PAGE_NEW, ActionDefinition::INDEX);
  574. $this->actionUpdate(
  575. $actions,
  576. Crud::PAGE_NEW,
  577. ActionDefinition::SAVE_AND_RETURN,
  578. [
  579. 'add_class' => 'float-right',
  580. 'icon' => 'check',
  581. 'label' => $this->translatorAdmin->transAction('save_and_return'),
  582. ]
  583. );
  584. $this->actionUpdate(
  585. $actions,
  586. Crud::PAGE_NEW,
  587. ActionDefinition::INDEX,
  588. [
  589. 'icon' => 'chevron-left',
  590. 'class' => 'btn btn-link',
  591. 'label' => $this->translatorAdmin->transAction('back_index'),
  592. ]
  593. );
  594. $this->actionUpdate(
  595. $actions,
  596. Crud::PAGE_NEW,
  597. ActionDefinition::SAVE_AND_ADD_ANOTHER,
  598. [
  599. 'class' => 'btn btn-info float-right',
  600. 'label' => $this->translatorAdmin->transAction('save_and_add_another'),
  601. ]
  602. );
  603. }
  604. public function handleTranslatableEntityActions(Actions $actions): void
  605. {
  606. if ($this->isInstanceOf(TranslatableInterface::class)) {
  607. $actions->update(
  608. Crud::PAGE_INDEX,
  609. ActionDefinition::EDIT,
  610. function (Action $action) {
  611. $action->setTemplatePath('@LcSov/adminlte/crud/action/translatable.html.twig');
  612. return $action;
  613. }
  614. );
  615. }
  616. }
  617. public function handleSortableEntityActions(Actions $actions): void
  618. {
  619. if ($this->isInstanceOf(SortableInterface::class)) {
  620. $sortAction = Action::new(
  621. ActionDefinition::SORT,
  622. $this->translatorAdmin->transAction(ActionDefinition::SORT),
  623. 'fa fa-sort'
  624. )
  625. ->linkToCrudAction(ActionDefinition::SORT)
  626. ->setCssClass('btn btn-sm btn-success')
  627. ->createAsGlobalAction();
  628. $actions->add(Crud::PAGE_INDEX, $sortAction);
  629. }
  630. }
  631. public function handleTreeEntityActions(Actions $actions): void
  632. {
  633. if ($this->isInstanceOf(TreeInterface::class)) {
  634. $indexChildAction = Action::new(
  635. ActionDefinition::INDEX_CHILDREN,
  636. $this->translatorAdmin->transAction(ActionDefinition::INDEX_CHILDREN),
  637. 'fa fa-list'
  638. )
  639. ->linkToCrudAction(ActionDefinition::INDEX)
  640. ->setLabel('')
  641. ->setHtmlAttributes(array('data-toggle' => 'tooltip', 'title' => 'Afficher les enfants'))
  642. ->setTemplatePath('@LcSov/adminlte/crud/action/index_children.html.twig')
  643. ->setCssClass('btn btn-sm btn-success');
  644. $backParentAction = Action::new(
  645. ActionDefinition::INDEX_PARENT,
  646. $this->getTranslatorAdmin()->transAction(ActionDefinition::INDEX_PARENT),
  647. 'fa fa-chevron-left'
  648. )
  649. ->linkToCrudAction(ActionDefinition::INDEX)
  650. ->setCssClass('btn btn-sm btn-info')
  651. ->createAsGlobalAction();
  652. $actions->add(Crud::PAGE_INDEX, $backParentAction);
  653. $actions->add(Crud::PAGE_INDEX, $indexChildAction);
  654. }
  655. }
  656. public function actionUpdate($actions, $crudActionName, $actionName, array $button): void
  657. {
  658. if ($actions->getAsDto('actions')->getAction($crudActionName, $actionName)) {
  659. $actions->update(
  660. $crudActionName,
  661. $actionName,
  662. function (Action $action) use ($button) {
  663. if (isset($button['add_class'])) {
  664. $action->addCssClass($button['add_class']);
  665. }
  666. if (isset($button['class'])) {
  667. $action->setCssClass($button['class']);
  668. }
  669. if (isset($button['icon'])) {
  670. $action->setIcon('fa fa-' . $button['icon']);
  671. }
  672. if (isset($button['label'])) {
  673. $action->setLabel($button['label']);
  674. }
  675. if (isset($button['dropdown']) && $button['dropdown']) {
  676. $action->addCssClass('in-dropdown');
  677. }
  678. if (isset($button['html_attributes']) && $button['html_attributes']) {
  679. $action->setHtmlAttributes($button['html_attributes']);
  680. }
  681. return $action;
  682. }
  683. );
  684. }
  685. }
  686. public function autocompleteFilter(AdminContext $context): JsonResponse
  687. {
  688. $repositoryQuery = $this->get(EntityRepository::class)->createRepositoryQuery(
  689. $this->getRepositoryQuery(),
  690. $context->getSearch(),
  691. $context->getEntity(),
  692. FieldCollection::new([]),
  693. FilterCollection::new()
  694. );
  695. if ($this->isInstanceOf(StatusInterface::class)) {
  696. $repositoryQuery->filterIsOnlineAndOffline();
  697. }
  698. $autocompleteContext = $context->getRequest()->get(AssociationField::PARAM_AUTOCOMPLETE_CONTEXT);
  699. /** @var CrudControllerInterface $controller */
  700. $controller = $this->get(ControllerFactory::class)->getCrudControllerInstance(
  701. $autocompleteContext[EA::CRUD_CONTROLLER_FQCN],
  702. ActionDefinition::INDEX,
  703. $context->getRequest()
  704. );
  705. /** @var FieldDto $field */
  706. $field = FieldCollection::new(
  707. $controller->configureFields($autocompleteContext['originatingPage'])
  708. )->getByProperty($autocompleteContext['propertyName']);
  709. $filterManager = $this->getFilterManager();
  710. $filteredValue = ['value' => $context->getRequest()->query->get('q')];
  711. $filterManager->applyFilter($repositoryQuery, $field, $filteredValue);
  712. $repositoryQuery->select('.' . $autocompleteContext['propertyName']);
  713. $responses = array();
  714. foreach ($repositoryQuery->find() as $result) {
  715. $responses[] = array_values($result)[0];
  716. }
  717. return JsonResponse::fromJsonString(json_encode($responses));
  718. }
  719. public function createCustomForm($class, $action, $controller, $entity, $dataInOption = true, $options = array())
  720. {
  721. if ($dataInOption) {
  722. $data = $entity;
  723. } else {
  724. $data = null;
  725. }
  726. $options['action'] = $this->get(AdminUrlGenerator::class)
  727. ->setAction($action)
  728. ->setController($controller)
  729. ->setEntityId($entity->getId())
  730. ->generateUrl();
  731. return $this->createForm($class, $data, $options);
  732. }
  733. public function eaBeforeCrudActionEventDelete(AdminContext $context): ?Response
  734. {
  735. $event = new BeforeCrudActionEvent($context);
  736. $this->container->get('event_dispatcher')->dispatch($event);
  737. if ($event->isPropagationStopped()) {
  738. return $event->getResponse();
  739. }
  740. if (!$this->isGranted(
  741. Permission::EA_EXECUTE_ACTION,
  742. ['action' => Action::DELETE, 'entity' => $context->getEntity()]
  743. )) {
  744. throw new ForbiddenActionException($context);
  745. }
  746. if (!$context->getEntity()->isAccessible()) {
  747. throw new InsufficientEntityPermissionException($context);
  748. }
  749. $csrfToken = $context->getRequest()->request->get('token');
  750. if (!$this->isCsrfTokenValid('ea-delete', $csrfToken)) {
  751. return $this->redirectToRoute($context->getDashboardRouteName());
  752. }
  753. return null;
  754. }
  755. }