diff --git a/src/Controller/GroupController.php b/src/Controller/GroupController.php index 87bf0df..be6c072 100644 --- a/src/Controller/GroupController.php +++ b/src/Controller/GroupController.php @@ -45,6 +45,7 @@ class GroupController extends AbstractController $group = new Group(); $group->addUser($this->getUser()); $group->setStatus(Group::ACTIVE); + $group->setOpen(true); $this->denyAccessUnlessGranted(GroupVoter::SUBMIT, $group); @@ -130,6 +131,25 @@ class GroupController extends AbstractController return $this->redirectToRoute($isAdmin ? 'app_admin_group' : 'app_user_group_view', ['id' => $group->getId()]); } + #[Route('/admin/group/subscribe/{id}', name: 'app_admin_group_subscribe')] + #[Route('/user/group/subscribe/{id}', name: 'app_user_group_subscribe')] + public function subscribe(int $id, Request $request, GroupRepository $groupRepository, EntityManagerInterface $em): Response + { + $group = $groupRepository->find($id); + if (!$group) { + throw new NotFoundHttpException('La ressource demandée est introuvable.'); + } + + $this->denyAccessUnlessGranted(GroupVoter::CANSUBSCRIBE, $group); + + $group->addUser($this->getUser()); + $em->flush(); + + $isAdmin = str_starts_with($request->attributes->get('_route'), 'app_admin'); + + return $this->redirectToRoute($isAdmin ? 'app_admin_group' : 'app_user_group_view', ['id' => $group->getId()]); + } + #[Route('/admin/group/delete/{id}', name: 'app_admin_group_delete')] #[Route('/user/group/delete/{id}', name: 'app_user_group_delete')] public function delete(int $id, Request $request, GroupRepository $groupRepository, EntityManagerInterface $em): Response @@ -174,6 +194,7 @@ class GroupController extends AbstractController 'routecancel' => $isAdmin ? 'app_admin_group' : 'app_home', 'routedelete' => $isAdmin ? 'app_admin_group_delete' : 'app_user_group_delete', 'routemove' => $isAdmin ? 'app_admin_group_move' : 'app_user_group_move', + 'routesubscribe' => $isAdmin ? 'app_admin_group_subscribe' : 'app_user_group_subscribe', 'group' => $group, ]); } diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php index 95a255f..8514656 100644 --- a/src/Controller/ProjectController.php +++ b/src/Controller/ProjectController.php @@ -47,6 +47,7 @@ class ProjectController extends AbstractController $project = new Project(); $project->addUser($this->getUser()); $project->setStatus(Project::DRAFT); + $project->setOpen(true); $this->denyAccessUnlessGranted(ProjectVoter::SUBMIT, $project); @@ -161,6 +162,25 @@ class ProjectController extends AbstractController return $this->redirectToRoute($isAdmin ? 'app_admin_project' : 'app_user_project_view', ['id' => $project->getId()]); } + #[Route('/admin/project/subscribe/{id}', name: 'app_admin_project_subscribe')] + #[Route('/user/project/subscribe/{id}', name: 'app_user_project_subscribe')] + public function subscribe(int $id, Request $request, ProjectRepository $projectRepository, EntityManagerInterface $em): Response + { + $project = $projectRepository->find($id); + if (!$project) { + throw new NotFoundHttpException('La ressource demandée est introuvable.'); + } + + $this->denyAccessUnlessGranted(ProjectVoter::CANSUBSCRIBE, $project); + + $project->addUser($this->getUser()); + $em->flush(); + + $isAdmin = str_starts_with($request->attributes->get('_route'), 'app_admin'); + + return $this->redirectToRoute($isAdmin ? 'app_admin_project' : 'app_user_project_view', ['id' => $project->getId()]); + } + #[Route('/admin/project/delete/{id}', name: 'app_admin_project_delete')] #[Route('/user/project/delete/{id}', name: 'app_user_project_delete')] public function delete(int $id, Request $request, ProjectRepository $projectRepository, EntityManagerInterface $em): Response @@ -205,6 +225,7 @@ class ProjectController extends AbstractController 'routecancel' => $isAdmin ? 'app_admin_project' : 'app_home', 'routedelete' => $isAdmin ? 'app_admin_project_delete' : 'app_user_project_delete', 'routemove' => $isAdmin ? 'app_admin_project_move' : 'app_user_project_move', + 'routesubscribe' => $isAdmin ? 'app_admin_project_subscribe' : 'app_user_project_subscribe', 'project' => $project, ]); } diff --git a/src/Entity/Group.php b/src/Entity/Group.php index 41d5e2b..aca2902 100644 --- a/src/Entity/Group.php +++ b/src/Entity/Group.php @@ -29,6 +29,9 @@ class Group #[ORM\Column(type: 'text', nullable: true)] private ?string $description = null; + #[ORM\Column(type: 'boolean', nullable: false)] + private bool $open; + #[ORM\Column] private string $status; @@ -95,6 +98,18 @@ class Group return $this; } + public function isOpen(): bool + { + return $this->open; + } + + public function setOpen(bool $open): static + { + $this->open = $open; + + return $this; + } + public function getStatus(): string { return $this->status; diff --git a/src/Entity/Project.php b/src/Entity/Project.php index c18e43e..1280c27 100644 --- a/src/Entity/Project.php +++ b/src/Entity/Project.php @@ -36,6 +36,9 @@ class Project #[ORM\Column(type: 'string', length: 20)] private string $nature; + #[ORM\Column(type: 'boolean', nullable: false)] + private bool $open; + #[ORM\Column] private string $status; @@ -131,6 +134,18 @@ class Project return $this; } + public function isOpen(): bool + { + return $this->open; + } + + public function setOpen(bool $open): static + { + $this->open = $open; + + return $this; + } + public function getStatus(): string { return $this->status; diff --git a/src/Form/GroupType.php b/src/Form/GroupType.php index b8e7518..73fc3f4 100644 --- a/src/Form/GroupType.php +++ b/src/Form/GroupType.php @@ -7,6 +7,7 @@ use App\Entity\User; use Bnine\MdEditorBundle\Form\Type\MarkdownType; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; @@ -38,8 +39,13 @@ class GroupType extends AbstractType 'label' => 'Résumé', ]) + ->add('open', CheckboxType::class, [ + 'label' => 'Groupe Ouvert', + 'required' => false, + ]) + ->add('users', EntityType::class, [ - 'label' => 'Propriétaires', + 'label' => 'Paticipants', 'class' => User::class, 'choice_label' => 'username', 'multiple' => true, diff --git a/src/Form/ProjectType.php b/src/Form/ProjectType.php index 95cb679..681dc88 100644 --- a/src/Form/ProjectType.php +++ b/src/Form/ProjectType.php @@ -7,6 +7,7 @@ use App\Entity\User; use Bnine\MdEditorBundle\Form\Type\MarkdownType; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; @@ -39,6 +40,11 @@ class ProjectType extends AbstractType 'label' => 'Résumé', ]) + ->add('open', CheckboxType::class, [ + 'label' => 'Projet Ouvert', + 'required' => false, + ]) + ->add('nature', ChoiceType::class, [ 'label' => 'Nature', 'choices' => [ @@ -49,7 +55,7 @@ class ProjectType extends AbstractType ]) ->add('users', EntityType::class, [ - 'label' => 'Propriétaires', + 'label' => 'Paticipants', 'class' => User::class, 'choice_label' => 'username', 'multiple' => true, diff --git a/src/Security/GroupVoter.php b/src/Security/GroupVoter.php index ebf190a..79a8427 100644 --- a/src/Security/GroupVoter.php +++ b/src/Security/GroupVoter.php @@ -17,10 +17,11 @@ class GroupVoter extends Voter public const MOVETOACTIVE = 'MOVETOACTIVE'; public const MOVETOINACTIVE = 'MOVETOINACTIVE'; public const TOME = 'TOME'; + public const CANSUBSCRIBE = 'CANSUBSCRIBE'; protected function supports(string $attribute, $subject): bool { - $attributes = [self::VIEW, self::SUBMIT, self::UPDATE, self::DELETE, self::MOVETOACTIVE, self::MOVETOINACTIVE, self::TOME]; + $attributes = [self::VIEW, self::SUBMIT, self::UPDATE, self::DELETE, self::MOVETOACTIVE, self::MOVETOINACTIVE, self::TOME, self::CANSUBSCRIBE]; return in_array($attribute, $attributes) && $subject instanceof Group; } @@ -76,6 +77,11 @@ class GroupVoter extends Voter return $group->getUsers()->contains($user); } + private function canSubscribe(Group $group, User $user): bool + { + return $group->isOpen() && !$group->getUsers()->contains($user); + } + protected function voteOnAttribute(string $attribute, $group, TokenInterface $token): bool { $user = $token->getUser(); @@ -91,6 +97,8 @@ class GroupVoter extends Voter self::MOVETOACTIVE => $this->canMoveToActive($group, $user), self::MOVETOINACTIVE => $this->canMoveToInactive($group, $user), self::TOME => $this->toMe($group, $user), + self::CANSUBSCRIBE => $this->canSubscribe($group, $user), + default => false, }; } diff --git a/src/Security/ProjectVoter.php b/src/Security/ProjectVoter.php index cc6e6c2..03b0d50 100644 --- a/src/Security/ProjectVoter.php +++ b/src/Security/ProjectVoter.php @@ -19,10 +19,11 @@ class ProjectVoter extends Voter public const MOVEVOTED = 'MOVEVOTED'; public const MOVEARCHIVED = 'MOVEARCHIVED'; public const TOME = 'TOME'; + public const CANSUBSCRIBE = 'CANSUBSCRIBE'; protected function supports(string $attribute, $subject): bool { - $attributes = [self::VIEW, self::SUBMIT, self::UPDATE, self::DELETE, self::MOVEDRAFT, self::MOVETOVOTE, self::MOVEVOTED, self::MOVEARCHIVED, self::TOME]; + $attributes = [self::VIEW, self::SUBMIT, self::UPDATE, self::DELETE, self::MOVEDRAFT, self::MOVETOVOTE, self::MOVEVOTED, self::MOVEARCHIVED, self::TOME, self::CANSUBSCRIBE]; return in_array($attribute, $attributes) && $subject instanceof Project; } @@ -97,6 +98,11 @@ class ProjectVoter extends Voter return $project->getUsers()->contains($user); } + private function canSubscribe(Project $project, User $user): bool + { + return $project->isOpen() && !$project->getUsers()->contains($user); + } + protected function voteOnAttribute(string $attribute, $project, TokenInterface $token): bool { $user = $token->getUser(); @@ -114,6 +120,7 @@ class ProjectVoter extends Voter self::MOVEVOTED => $this->canMoveVoted($project, $user), self::MOVEARCHIVED => $this->canMoveArchived($project, $user), self::TOME => $this->toMe($project, $user), + self::CANSUBSCRIBE => $this->canSubscribe($project, $user), default => false, }; } diff --git a/templates/group/edit.html.twig b/templates/group/edit.html.twig index 99aa6d5..e024a0b 100644 --- a/templates/group/edit.html.twig +++ b/templates/group/edit.html.twig @@ -20,6 +20,7 @@