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.

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