diff --git a/migrations/Version20220506101824.php b/migrations/Version20220506101824.php new file mode 100644 index 0000000..111659f --- /dev/null +++ b/migrations/Version20220506101824.php @@ -0,0 +1,38 @@ +addSql('CREATE TABLE project (id INT AUTO_INCREMENT NOT NULL, owner_id INT DEFAULT NULL, `key` VARCHAR(5) NOT NULL, name VARCHAR(255) NOT NULL, INDEX IDX_2FB3D0EE7E3C61F9 (owner_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE project ADD CONSTRAINT FK_2FB3D0EE7E3C61F9 FOREIGN KEY (owner_id) REFERENCES user (id)'); + $this->addSql('ALTER TABLE task ADD project_id INT DEFAULT NULL, CHANGE is_completed is_completed TINYINT(1) DEFAULT 0 NOT NULL'); + $this->addSql('ALTER TABLE task ADD CONSTRAINT FK_527EDB25166D1F9C FOREIGN KEY (project_id) REFERENCES project (id)'); + $this->addSql('CREATE INDEX IDX_527EDB25166D1F9C ON task (project_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE task DROP FOREIGN KEY FK_527EDB25166D1F9C'); + $this->addSql('DROP TABLE project'); + $this->addSql('DROP INDEX IDX_527EDB25166D1F9C ON task'); + $this->addSql('ALTER TABLE task DROP project_id, CHANGE is_completed is_completed TINYINT(1) DEFAULT NULL'); + } +} diff --git a/migrations/Version20220506111051.php b/migrations/Version20220506111051.php new file mode 100644 index 0000000..eccb6aa --- /dev/null +++ b/migrations/Version20220506111051.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE project CHANGE `key` `key` VARCHAR(255) NOT NULL'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_2FB3D0EE8A90ABA9 ON project (`key`)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP INDEX UNIQ_2FB3D0EE8A90ABA9 ON project'); + $this->addSql('ALTER TABLE project CHANGE `key` `key` VARCHAR(5) NOT NULL'); + } +} diff --git a/migrations/Version20220506132158.php b/migrations/Version20220506132158.php new file mode 100644 index 0000000..6b8c35a --- /dev/null +++ b/migrations/Version20220506132158.php @@ -0,0 +1,35 @@ +addSql('DROP INDEX UNIQ_2FB3D0EE8A90ABA9 ON project'); + $this->addSql('ALTER TABLE project CHANGE `key` project_key VARCHAR(255) NOT NULL'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_2FB3D0EE18EB714A ON project (project_key)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP INDEX UNIQ_2FB3D0EE18EB714A ON project'); + $this->addSql('ALTER TABLE project CHANGE project_key `key` VARCHAR(255) NOT NULL'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_2FB3D0EE8A90ABA9 ON project (`key`)'); + } +} diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php new file mode 100644 index 0000000..e577f58 --- /dev/null +++ b/src/Controller/ProjectController.php @@ -0,0 +1,142 @@ +getUser(); + $projectRepository = $this->getDoctrine() -> getManager() -> getRepository(Project::class); + + $projects = $projectRepository->findByUserRole($user->getId()); + + return $this -> render("projects/list.html.twig", [ + "projects" => $projects, + ]); + } + + /** + * @Route("/projects/create", name="project_create") + * @param Request $request + * @return Response + */ + public function create(Request $request): Response + { + $project = new Project(); + $form = $this -> createForm(ProjectType::class, $project); + + $form -> handleRequest($request); + + if ($form -> isSubmitted() && $form -> isValid()) { + + $project -> setOwner($this -> getUser()); + + $this -> getDoctrine() -> getManager() -> persist($project); + $this -> getDoctrine() -> getManager() -> flush(); + + return $this -> redirectToRoute("project_list"); + } + + return $this -> render("projects/create.html.twig", [ + "form" => $form -> createView(), + "action" => "create" + ]); + } + + /** + * @Route("/projects/{slug}", name="project_show") + * @return Response + */ + public function show($slug, Request $request): Response + { + $project = $this->getDoctrine()->getRepository(Project::class)->findOneBy(["projectKey" => $slug]); +// dd($project); + + $this->denyAccessUnlessGranted('view', $project); + + if ($project === null) { + throw $this->createNotFoundException(sprintf("Project with key %s not found", $slug)); + } + + $tasks = $project -> getTasks(); + + return $this->render('task/project_tasks.html.twig', [ + 'tasks' => $tasks, + "project_name" => $project -> getName(), + ]); + } + + /** + * @Route("/projects/{slug}/edit", name="project_edit") + * @param $slug + * @param Request $request + * @return Response + */ + public function edit($slug, Request $request): Response + { + $project = $this->getDoctrine()->getRepository(Project::class) -> findOneBy(["projectKey" => $slug]); + + $this->denyAccessUnlessGranted('edit', $project); + + if ($project === null) { + throw $this->createNotFoundException(sprintf("Project with key %s not found", $slug)); + } + + $form = $this->createForm(ProjectType::class, $project); + + $form -> handleRequest($request); + + if ($form -> isSubmitted() && $form -> isValid()) { + $this -> getDoctrine() -> getManager() -> persist($project); + $this -> getDoctrine() -> getManager() -> flush(); + + return $this -> redirectToRoute("project_list"); + } + + return $this->render("projects/create.html.twig", [ + "form" => $form->createView(), + "action" => "edit", + "slug" => $slug + ]); + } + + /** + * @Route("/projects/{slug}/delete", name="project_delete") + * @param $slug + * @param Request $request + * @return Response + */ + public function delete($slug, Request $request): Response + { + $em = $this->getDoctrine()->getManager(); + $project = $this->getDoctrine()->getRepository( + Project::class) -> findOneBy(["projectKey" => $slug]); + + $this->denyAccessUnlessGranted('delete', $project); + + if ($project === null) { + throw $this->createNotFoundException(sprintf("Project with key %s not found", $slug)); + } + + $em -> remove($project); + $em -> flush(); + + return $this->redirectToRoute("project_list"); + } +} \ No newline at end of file diff --git a/src/Controller/TaskController.php b/src/Controller/TaskController.php index 0d9f9ad..a9230e6 100644 --- a/src/Controller/TaskController.php +++ b/src/Controller/TaskController.php @@ -2,14 +2,20 @@ namespace App\Controller; +use App\Entity\Project; use App\Entity\Task; +use App\Entity\User; use App\Type\TaskFilterType; use App\Type\TaskType; +use Doctrine\Common\Collections\Criteria; +use phpDocumentor\Reflection\Types\Integer; +use PhpParser\Node\Expr\Array_; use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\HttpFoundation\Request; +use const http\Client\Curl\PROXY_HTTP; class TaskController extends AbstractController { @@ -22,12 +28,17 @@ class TaskController extends AbstractController public function create(Request $request): Response { $task = new Task(); - $form = $this->createForm(TaskType::class, $task); + $user = $this -> getUser(); + $projectRepository = $this -> getDoctrine() -> getRepository(Project::class); + + $form = $this->createForm(TaskType::class, $task, [ + "projectsList" => $projectRepository -> findByUserRole($user -> getId()), +// 'userId' => 4, + ]); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - $task->setAuthor($this->getUser()); $this->getDoctrine()->getManager()->persist($task); @@ -37,9 +48,9 @@ public function create(Request $request): Response } return $this->render("task/create.html.twig", [ - 'form' => $form->createView() + 'form' => $form->createView(), + "action" => "create", ]); - } /** @@ -48,36 +59,36 @@ public function create(Request $request): Response */ public function list(Request $request): Response { - $taskFilterForm = $this->createForm(TaskFilterType::class); + $user = $this -> getUser(); + $userIsAdmin = $this -> isGranted("ROLE_ADMIN"); + + $projectRepository = $this -> getDoctrine() -> getRepository(Project::class); + $userRepository = $this->getDoctrine()->getRepository(User::class); + $projectsList = $projectRepository -> findByUserRole($user -> getId()); + $projectsIdList = array_map(function ($item) { return $item->getId(); }, $projectsList); + + $taskFilterForm = $this->createForm(TaskFilterType::class, options: [ + "projectsList" => $projectsList, + "authorsList" => $userRepository->findByTaskAuthorshipInProjectList($projectsIdList), + "ownersList" => $userRepository->findByProjectOwnershipInProjectList($projectsIdList), + ]); $taskFilterForm->handleRequest($request); + $taskRepository = $this->getDoctrine()->getRepository(Task::class); if ($taskFilterForm->isSubmitted() && $taskFilterForm->isValid()) { - - $filter = $taskFilterForm->getData(); - if ($filter['isCompleted'] === null) { - unset($filter['isCompleted']); - } - - $tasks = $this->getDoctrine()->getRepository(Task::class) - ->findBy($filter, [ - 'dueDate' => 'DESC' - ]); - + $tasks = $taskRepository -> findByFilterData($taskFilterForm->getData(), + !($this->isGranted("ROLE_ADMIN")) ? $this->getUser()->getId() : null); } else { - /** @var $tasks */ - $tasks = $this->getDoctrine()->getManager() - ->getRepository(Task::class) - ->findBy([], [ - 'dueDate' => 'DESC' - ]); + $tasks = $userIsAdmin ? + $taskRepository -> findBy([], ["dueDate" => "DESC"]) : + $taskRepository -> findByProjectOwnerId($user -> getId()); +// dd($tasks); } - - return $this->render('task/list.html.twig', [ 'tasks' => $tasks, - 'filterForm' => $taskFilterForm->createView() + 'filterForm' => $taskFilterForm->createView(), ]); } @@ -104,4 +115,43 @@ public function complete($id): Response return $this->redirectToRoute('task_list'); } + + /** + * @Route("/tasks/{id}/edit", name="task_edit") + * @param $id + * @param Request $request + * @return Response + */ + public function edit($id, Request $request): Response + { + $task = $this->getDoctrine()->getManager()->find(Task::class, $id); + + $this->denyAccessUnlessGranted('edit', $task); + + if ($task === null) { + throw $this->createNotFoundException(sprintf("Task with id %s not found", $id)); + } + + $user = $this -> getUser(); + $projectRepository = $this -> getDoctrine() -> getRepository(Project::class); + + $form = $this->createForm(TaskType::class, data: $task, options: [ + "projectsList" => $projectRepository -> findByUserRole($user -> getId()), + ]); + + $form -> handleRequest($request); + + if ($form -> isSubmitted() && $form -> isValid()) { + $this->getDoctrine()->getManager()->persist($task); + $this->getDoctrine()->getManager()->flush(); + + return $this->redirectToRoute('task_list'); + } + + return $this -> render('task/create.html.twig', [ + "form" => $form -> createView(), + "action" => "edit", + "id" => $id + ]); + } } \ No newline at end of file diff --git a/src/Entity/Project.php b/src/Entity/Project.php new file mode 100644 index 0000000..5e28c81 --- /dev/null +++ b/src/Entity/Project.php @@ -0,0 +1,126 @@ +tasks = new ArrayCollection(); + } + + /** + * @return Collection|Task[] + */ + public function getTasks(): Collection + { + return $this->tasks; + } + + public function __toString() + { + return $this->getName(); + } + + /** + * set project Owner + * @param User | null $owner + * @return void + */ + public function setOwner(UserInterface $owner = null) + { + $this -> owner = $owner; + } + + /** + * return project owner + * @return User | null + */ + public function getOwner(): ?User + { + return $this -> owner; + } + + /** + * @return mixed + */ + public function getName() + { + return $this -> name; + } + + /** + * @return mixed + */ + public function getId() + { + return $this -> id; + } + + /** + * @param mixed $name + */ + public function setName($name): void + { + $this -> name = $name; + } + + /** + * @return mixed + */ + public function getProjectKey() + { + return $this -> projectKey; + } + + /** + * @param mixed $projectKey + */ + public function setProjectKey($projectKey): void + { + $this -> projectKey = $projectKey; + } +} \ No newline at end of file diff --git a/src/Entity/Task.php b/src/Entity/Task.php index 66a4b57..d28097f 100644 --- a/src/Entity/Task.php +++ b/src/Entity/Task.php @@ -4,6 +4,7 @@ use App\Repository\TaskRepository; use Symfony\Component\Security\Core\User\UserInterface; +//use Symfony\Component\Security\Core\; use Symfony\Component\Validator\Constraints as Assert; use Doctrine\ORM\Mapping as ORM; @@ -55,6 +56,12 @@ class Task */ protected $author; + /** + * @ORM\ManyToOne(targetEntity="App\Entity\Project", inversedBy="project") + * @var Project + */ + protected $project; + /** * Create empty task */ @@ -75,16 +82,35 @@ public function setAuthor(UserInterface $author = null) $this->author = $author; } - /** * Return task author * @return User|null */ - public function getAuthor() + public function getAuthor(): ?User { return $this->author; } + /** + * Set task Project + * @param Project|null $project + * @return void + */ + public function setProject(Project $project = null) + { + $this->project = $project; + } + + + /** + * Return task Project + * @return Project|null + */ + public function getProject(): ?Project + { + return $this->project; + } + /** * @return mixed diff --git a/src/Entity/User.php b/src/Entity/User.php index 98648d0..b3c0923 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -79,6 +79,19 @@ public function getRoles(): array return array_unique($roles); } + /** + * @param string $role + * @return bool + */ + public function hasRole(string $role): bool + { + return in_array($role, $this -> roles); + } + + /** + * @param array $roles + * @return $this + */ public function setRoles(array $roles): self { $this->roles = $roles; diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php new file mode 100644 index 0000000..120f46e --- /dev/null +++ b/src/Repository/ProjectRepository.php @@ -0,0 +1,28 @@ +getEntityManager()->find(User::class, $id)->hasRole("ROLE_ADMIN")) { + return $this->findAll(); + } else { + return $this->findBy(["owner" => $id]); + } + } +} \ No newline at end of file diff --git a/src/Repository/TaskRepository.php b/src/Repository/TaskRepository.php index 43cc1ba..a6dcb04 100644 --- a/src/Repository/TaskRepository.php +++ b/src/Repository/TaskRepository.php @@ -4,7 +4,9 @@ use App\Entity\Task; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\DBAL\Exception; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Query; use Doctrine\Persistence\ManagerRegistry; class TaskRepository extends ServiceEntityRepository @@ -14,5 +16,63 @@ public function __construct(ManagerRegistry $registry) parent::__construct($registry, Task::class); } + /** + * @throws Exception + */ + public function findByProjectOwnerId(int $id) : array + { + $conn = $this -> getEntityManager() -> getConnection(); + + $sql = " + SELECT t.id, t.name, t.description, t.due_date as dueDate, + u.email as author, t.is_completed as isCompleted + FROM task as t INNER JOIN user as u ON t.author_id = u.id + WHERE project_id in (SELECT id FROM project WHERE owner_id = :id); + "; + $stmt = $conn -> prepare($sql); + return $stmt ->executeQuery(["id" => $id]) -> fetchAllAssociative(); + } + + public function getTasksAuthors() : array + { + return $this->createQueryBuilder("t") + ->select(["distinct u.email", "u.id"]) + ->innerJoin("t.author", "u") + ->getQuery() + ->getArrayResult(); + } + + /** + * @throws Exception + */ + public function findByFilterData(array $data, ?int $user_id) : array + { + $qb = $this->createQueryBuilder("t"); + $qb->select("t"); + $qb->join("App\Entity\User", "u", "WITH", "t.author = u.id"); + $qb->join("App\Entity\Project", "p", "WITH", "t.project = p.id"); + + if ($user_id !== null) { + $qb->add("where", $qb->expr()->eq("p.owner", $user_id)); + } + + if ($data["project"] !== null) { + $qb->add("where", $qb->expr()->eq("t.project", $data["project"]->getId())); + } + + if ($data["author"] !== null) { + $qb->add("where", $qb->expr()->eq("u.author", $data["author"]->getId())); + } + + if ($data["project-owner"] !== null) { + $qb->add("where", $qb->expr()->eq("p.owner", $data["owner"]->getId())); + } + + $qb->orderBy("t.dueDate", $data["sort-by-date"] ? "ASC" : "DESC"); + $qb->orderBy("t.name", $data["sort-by-name"] ? "ASC" : "DESC"); + + return $qb->getQuery()->getResult(); + } + } \ No newline at end of file diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index aabb343..269475a 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -2,8 +2,10 @@ namespace App\Repository; +use App\Entity\Task; use App\Entity\User; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\DBAL\Result; use Doctrine\ORM\OptimisticLockException; use Doctrine\ORM\ORMException; use Doctrine\Persistence\ManagerRegistry; @@ -90,4 +92,30 @@ public function findOneBySomeField($value): ?User ; } */ + + public function findByTaskAuthorshipInProjectList(?array $projectsIdList): array + { + $qb = $this->createQueryBuilder("u"); + return $qb + ->select("u") + ->distinct() + ->innerJoin("App\Entity\Task", "t", "WITH", "u.id = t.author") + ->where($qb->expr()->in("t.project", $projectsIdList)) + ->getQuery() + ->getResult(); + } + + public function findByProjectOwnershipInProjectList(?array $projectsIdtList): array + { + $qb = $this->createQueryBuilder("u"); + + return $qb + ->select("u") + ->distinct() + ->innerJoin("App\Entity\Project", "p", "WITH", "u.id = p.owner") + ->where($qb->expr()->in("p.id", $projectsIdtList)) + ->getQuery() + ->getResult(); + } + } diff --git a/src/Type/ProjectType.php b/src/Type/ProjectType.php new file mode 100644 index 0000000..219ca97 --- /dev/null +++ b/src/Type/ProjectType.php @@ -0,0 +1,25 @@ +add('project_key', TextType::class, [ + "label" => "Введите slug ключ" + ]) + ->add('name', TextType::class, [ + "label" => "Название проекта" + ]) + ->add('save', SubmitType::class) + ; + } + +} \ No newline at end of file diff --git a/src/Type/TaskFilterType.php b/src/Type/TaskFilterType.php index 885d59a..1062708 100644 --- a/src/Type/TaskFilterType.php +++ b/src/Type/TaskFilterType.php @@ -2,28 +2,85 @@ namespace App\Type; +use App\Entity\Project; 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\SubmitType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; class TaskFilterType extends AbstractType { + public function configureOptions(OptionsResolver $resolver) + { +// parent::configureOptions($resolver); // TODO: Change the autogenerated stub + + $resolver->setDefaults([ + 'projectsList' => [], + "authorsList" => [], + "ownersList" => [] + ]); + + $resolver->setAllowedTypes('projectsList', 'array'); + $resolver->setAllowedTypes('authorsList', 'array'); + $resolver->setAllowedTypes('ownersList', 'array'); + + } public function buildForm(FormBuilderInterface $builder, array $options) { +// dd($options); $builder ->setMethod('GET') ->add('isCompleted', ChoiceType::class, [ - 'choices' => [ - 'Да' => true, - 'Нет' => false, - 'Любое' => null - ], - 'label' => 'Выполнена' - ]) + 'choices' => [ + 'Да' => true, + 'Нет' => false, + 'Любое' => null, + ], + 'label' => 'Выполнена' + ]) + ->add("project", ChoiceType::class, [ + "placeholder" => "Любой", + "required" => false, + "choices" => $options["projectsList"], + "choice_label" => function(?Project $entity) { + return $entity ? $entity -> getProjectKey() : ""; + }, + ]) + ->add("author", ChoiceType::class, [ + "placeholder" => "Любой", + "required" => false, + "choices" => $options["authorsList"], + "choice_label" => function ($choice) { + return $choice; + } + ]) + ->add("project-owner", ChoiceType::class, [ + "placeholder" => "Любой", + "required" => false, + "choices" => $options["ownersList"], + "choice_label" => function ($choice) { + return $choice; + } + ]) + ->add("sort-by-name", ChoiceType::class, [ + "choices" => [ + "В лексикографическом порядке" => true, + "В обратном порядке" => false + ], + "label" => "Отсортировать по названию" + ]) + ->add("sort-by-date", ChoiceType::class, [ + "choices" => [ + "От старых к новым" => true, + "От новых к сатрым" => false + ], + "label" => "Отсортировать по дате" + ]) ->add('submit', SubmitType::class, [ - 'label' => 'Отфильтровать' + 'label' => 'Применить введённые данные' ]); } diff --git a/src/Type/TaskType.php b/src/Type/TaskType.php index 3b58193..7975d07 100644 --- a/src/Type/TaskType.php +++ b/src/Type/TaskType.php @@ -2,15 +2,33 @@ namespace App\Type; +use App\Entity\Project; +use App\Entity\User; +use Doctrine\ORM\EntityRepository; +use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; class TaskType extends AbstractType { + public function configureOptions(OptionsResolver $resolver) + { +// parent::configureOptions($resolver); // TODO: Change the autogenerated stub + + $resolver->setDefaults([ + 'projectsList' => [], + ]); + + $resolver->setAllowedTypes('projectsList', 'array'); + + } + public function buildForm(FormBuilderInterface $builder, array $options) { $builder @@ -19,6 +37,13 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->add('dueDate', DateType::class, [ 'years' => range(2022,2023) ]) + ->add('project', ChoiceType::class, [ + "choices" => $options["projectsList"], + 'choice_label' => function ($choice) { + return $choice; + }, + "label" => "Выберите проект", + ]) ->add('save', SubmitType::class) ; } diff --git a/src/Voter/ProjectVoter.php b/src/Voter/ProjectVoter.php new file mode 100644 index 0000000..3ec60ab --- /dev/null +++ b/src/Voter/ProjectVoter.php @@ -0,0 +1,45 @@ + getUser(); + + if (!$user instanceof User) { + return false; + } + + $isOwner = $subject->getOwner() === $user; + $isAdmin = $user->hasRole("ROLE_ADMIN"); + + return $isAdmin || $isOwner; + } + +} \ No newline at end of file diff --git a/src/Voter/TaskVoter.php b/src/Voter/TaskVoter.php index 1f84b73..6e1d5da 100644 --- a/src/Voter/TaskVoter.php +++ b/src/Voter/TaskVoter.php @@ -13,12 +13,13 @@ class TaskVoter extends Voter const COMPLETE = 'complete'; const DELETE = 'delete'; + const EDIT = "edit"; protected function supports($attribute, $subject) { - if (!in_array($attribute, [self::COMPLETE, self::DELETE])) { + if (!in_array($attribute, [self::COMPLETE, self::DELETE, self::EDIT])) { return false; } @@ -44,10 +45,11 @@ protected function voteOnAttribute($attribute, $subject, TokenInterface $token) return false; } - $isAdmin = in_array('ROLE_ADMIN', $user->getRoles()); - $isAuthor = ($subject->getAuthor() === $user); + $isAdmin = $user->hasRole("ROLE_ADMIN"); +// $isAuthor = ($subject->getAuthor() === $user); + $isProjectOwner = ($subject->getProject()->getOwner() === $user); - return $isAdmin || $isAuthor; + return $isAdmin || $isProjectOwner; } diff --git a/templates/projects/create.html.twig b/templates/projects/create.html.twig new file mode 100644 index 0000000..6852b7b --- /dev/null +++ b/templates/projects/create.html.twig @@ -0,0 +1,16 @@ +{% extends "base.html.twig" %} + +{% block body %} +
| Ключ | +Название | + {% if admin %} +Владелец | + {% endif %} +Действия | +
|---|---|---|---|
| + {% if (is_granted("view", project)) %} + + {{ project.projectKey }} + {% else %} + {{ project.projectKey }} + {% endif %} + | +{{ project.name }} | + {% if admin %} +{{ project.owner }} | + {% endif %} +
+ {% if (is_granted("delete", project)) %}
+
+ Удалить + {% endif %} + {% if (is_granted("edit", project)) %} + Edit + {% endif %} + |
+
| Название | +Описание | +Срок выполнения | +Выполнена | +
|---|---|---|---|
| {{ task.name }} | +{{ task.description }} | +{{ task.dueDate|date("d.m.Y") }} | +{{ task.isCompleted ? 'Выполнена' : 'Не выполнена' }} | +