diff --git a/src/Bundle/ChillEventBundle/Controller/EventThemeController.php b/src/Bundle/ChillEventBundle/Controller/EventThemeController.php new file mode 100644 index 000000000..027b99241 --- /dev/null +++ b/src/Bundle/ChillEventBundle/Controller/EventThemeController.php @@ -0,0 +1,44 @@ + 'create']); + } + + if ('edit' === $action) { + return parent::createFormFor($action, $entity, $formClass, ['step' => 'edit']); + } + + throw new \LogicException('action is not supported: '.$action); + } + + /** + * @param QueryBuilder|mixed $query + */ + protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator): QueryBuilder + { + /* @var QueryBuilder $query */ + return $query->orderBy('e.ordering', 'ASC') + ->addOrderBy('e.id', 'ASC'); + } +} diff --git a/src/Bundle/ChillEventBundle/DependencyInjection/ChillEventExtension.php b/src/Bundle/ChillEventBundle/DependencyInjection/ChillEventExtension.php index 0b30ca6c5..ecc072a58 100644 --- a/src/Bundle/ChillEventBundle/DependencyInjection/ChillEventExtension.php +++ b/src/Bundle/ChillEventBundle/DependencyInjection/ChillEventExtension.php @@ -11,6 +11,9 @@ declare(strict_types=1); namespace Chill\EventBundle\DependencyInjection; +use Chill\EventBundle\Controller\EventThemeController; +use Chill\EventBundle\Entity\EventTheme; +use Chill\EventBundle\Form\EventThemeType; use Chill\EventBundle\Security\EventVoter; use Chill\EventBundle\Security\ParticipationVoter; use Symfony\Component\Config\FileLocator; @@ -26,7 +29,10 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension; */ class ChillEventExtension extends Extension implements PrependExtensionInterface { - public function load(array $configs, ContainerBuilder $container) + /** + * @throws \Exception + */ + public function load(array $configs, ContainerBuilder $container): void { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); @@ -45,16 +51,17 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface /** (non-PHPdoc). * @see \Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface::prepend() */ - public function prepend(ContainerBuilder $container) + public function prepend(ContainerBuilder $container): void { $this->prependAuthorization($container); + $this->prependCruds($container); $this->prependRoute($container); } /** * add authorization hierarchy. */ - protected function prependAuthorization(ContainerBuilder $container) + protected function prependAuthorization(ContainerBuilder $container): void { $container->prependExtensionConfig('security', [ 'role_hierarchy' => [ @@ -70,7 +77,7 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface /** * add route to route loader for chill. */ - protected function prependRoute(ContainerBuilder $container) + protected function prependRoute(ContainerBuilder $container): void { // add routes for custom bundle $container->prependExtensionConfig('chill_main', [ @@ -81,4 +88,33 @@ class ChillEventExtension extends Extension implements PrependExtensionInterface ], ]); } + + protected function prependCruds(ContainerBuilder $container): void + { + $container->prependExtensionConfig('chill_main', [ + 'cruds' => [ + [ + 'class' => EventTheme::class, + 'name' => 'event_theme', + 'base_path' => '/admin/event/theme', + 'form_class' => EventThemeType::class, + 'controller' => EventThemeController::class, + 'actions' => [ + 'index' => [ + 'template' => '@ChillEvent/Admin/EventTheme/index.html.twig', + 'role' => 'ROLE_ADMIN', + ], + 'new' => [ + 'role' => 'ROLE_ADMIN', + 'template' => '@ChillEvent/Admin/EventTheme/new.html.twig', + ], + 'edit' => [ + 'role' => 'ROLE_ADMIN', + 'template' => '@ChillEvent/Admin/EventTheme/edit.html.twig', + ], + ], + ], + ], + ]); + } } diff --git a/src/Bundle/ChillEventBundle/Entity/EventTheme.php b/src/Bundle/ChillEventBundle/Entity/EventTheme.php new file mode 100644 index 000000000..902fc32fb --- /dev/null +++ b/src/Bundle/ChillEventBundle/Entity/EventTheme.php @@ -0,0 +1,183 @@ + + */ + #[ORM\OneToMany(mappedBy: 'parent', targetEntity: EventTheme::class)] + private Collection $children; + + #[ORM\ManyToOne(targetEntity: EventTheme::class, inversedBy: 'children')] + private ?EventTheme $parent = null; + + #[ORM\Column(name: 'ordering', type: Types::FLOAT, options: ['default' => '0.0'])] + private float $ordering = 0.0; + + /** + * Constructor. + */ + public function __construct() + { + $this->children = new ArrayCollection(); + } + + /** + * Get active. + */ + public function getIsActive(): bool + { + return $this->isActive; + } + + /** + * Get id. + */ + public function getId(): ?int + { + return $this->id; + } + + /** + * Get label. + */ + public function getName(): array + { + return $this->name; + } + + /** + * Set active. + * + * @param bool $active + * @return EventTheme + */ + public function setIsActive(bool $active): static + { + $this->isActive = $active; + + return $this; + } + + /** + * Set label. + * + * @param array $label + * @return EventTheme + */ + public function setName(array $label): static + { + $this->name = $label; + + return $this; + } + + public function addChild(self $child): self + { + if (!$this->children->contains($child)) { + $this->children[] = $child; + } + + return $this; + } + + public function removeChild(self $child): self + { + if ($this->children->removeElement($child)) { + // set the owning side to null (unless already changed) + if ($child->getParent() === $this) { + $child->setParent(null); + } + } + + return $this; + } + + public function getChildren(): Collection + { + return $this->children; + } + + public function getDescendants(): Collection + { + $descendants = new ArrayCollection(); + + foreach ($this->getChildren() as $child) { + if (!$descendants->contains($child)) { + $descendants->add($child); + + foreach ($child->getDescendants() as $descendantsOfChild) { + if (!$descendants->contains($descendantsOfChild)) { + $descendants->add($descendantsOfChild); + } + } + } + } + + return $descendants; + } + + public function hasParent(): bool + { + return null !== $this->parent; + } + + public function getOrdering(): float + { + return $this->ordering; + } + + public function setOrdering(float $ordering): EventTheme + { + $this->ordering = $ordering; + + return $this; + } + + public function getParent(): ?self + { + return $this->parent; + } + + public function setParent(?self $parent): self + { + $this->parent = $parent; + + $parent?->addChild($this); + + return $this; + } +} diff --git a/src/Bundle/ChillEventBundle/Form/EventThemeType.php b/src/Bundle/ChillEventBundle/Form/EventThemeType.php new file mode 100644 index 000000000..f48780641 --- /dev/null +++ b/src/Bundle/ChillEventBundle/Form/EventThemeType.php @@ -0,0 +1,67 @@ +add('name', TranslatableStringFormType::class, [ + 'label' => 'Nom', + ]); + + if ('create' === $options['step']) { + $builder + ->add('parent', EntityType::class, [ + 'class' => EventTheme::class, + 'required' => false, + 'choice_label' => fn (EventTheme $theme): ?string => $this->translatableStringHelper->localize($theme->getName()), + 'mapped' => 'create' === $options['step'], + ]); + } + + $builder + ->add('ordering', NumberType::class, [ + 'required' => true, + 'scale' => 6, + ]) + ->add('isActive', ChoiceType::class, [ + 'choices' => [ + 'Yes' => true, + 'No' => false, + ], + 'expanded' => true, + ]); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => EventTheme::class, + ]); + $resolver->setRequired('step') + ->setAllowedValues('step', ['create', 'edit']); + } +} diff --git a/src/Bundle/ChillEventBundle/Menu/AdminMenuBuilder.php b/src/Bundle/ChillEventBundle/Menu/AdminMenuBuilder.php index 9db1713b6..8a6dec1ad 100644 --- a/src/Bundle/ChillEventBundle/Menu/AdminMenuBuilder.php +++ b/src/Bundle/ChillEventBundle/Menu/AdminMenuBuilder.php @@ -20,14 +20,14 @@ class AdminMenuBuilder implements LocalMenuBuilderInterface /** * @var AuthorizationCheckerInterface */ - protected $authorizationChecker; + protected AuthorizationCheckerInterface $authorizationChecker; public function __construct(AuthorizationCheckerInterface $authorizationChecker) { $this->authorizationChecker = $authorizationChecker; } - public function buildMenu($menuId, MenuItem $menu, array $parameters) + public function buildMenu($menuId, MenuItem $menu, array $parameters): void { if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) { return; @@ -52,6 +52,10 @@ class AdminMenuBuilder implements LocalMenuBuilderInterface $menu->addChild('Role', [ 'route' => 'chill_event_admin_role', ])->setExtras(['order' => 6530]); + + $menu->addChild('Theme', [ + 'route' => 'chill_crud_event_theme_index', + ])->setExtras(['order' => 6540]); } public static function getMenuIds(): array diff --git a/src/Bundle/ChillEventBundle/Resources/views/Admin/EventTheme/edit.html.twig b/src/Bundle/ChillEventBundle/Resources/views/Admin/EventTheme/edit.html.twig new file mode 100644 index 000000000..a63d81c99 --- /dev/null +++ b/src/Bundle/ChillEventBundle/Resources/views/Admin/EventTheme/edit.html.twig @@ -0,0 +1,26 @@ +{% extends '@ChillMain/CRUD/Admin/index.html.twig' %} + +{% block title %} + {% include('@ChillMain/CRUD/_edit_title.html.twig') %} +{% endblock %} + +{% block admin_content %} + {% embed '@ChillMain/CRUD/_edit_content.html.twig' %} + + {% block crud_content_form_rows %} + {{ form_row(form.name) }} +