選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

457 行
17KB

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