diff --git a/Controller/DefaultController.php b/Controller/DefaultController.php
deleted file mode 100644
index 74b3aa34e..000000000
--- a/Controller/DefaultController.php
+++ /dev/null
@@ -1,13 +0,0 @@
-?php
-
-namespace Chill\TaskBundle\Controller;
-
-use Symfony\Bundle\FrameworkBundle\Controller\Controller;
-
-class DefaultController extends Controller
-{
- public function indexAction()
- {
- return $this->render('ChillTaskBundle:Default:index.html.twig');
- }
-}
diff --git a/Controller/SingleTaskController.php b/Controller/SingleTaskController.php
new file mode 100644
index 000000000..b2158d0bd
--- /dev/null
+++ b/Controller/SingleTaskController.php
@@ -0,0 +1,101 @@
+em = $em;
+ $this->formFactory = $formFactory;
+ }*/
+
+ /**
+ * @Route("/{_locale}/task/single-task/new")
+ */
+ public function newAction(Request $request)
+ {
+ $personId = $request->query->getInt('person_id', null);
+
+ if ($personId === null) {
+ return new Response("You must provide a person_id", Response::HTTP_BAD_REQUEST);
+ }
+
+ $person = $this->getDoctrine()->getManager()
+ ->getRepository(Person::class)
+ ->find($personId);
+
+ if ($person === null) {
+ throw $this->createNotFoundException("Invalid person id");
+ }
+
+ $task = (new SingleTask())
+ ->setPerson($person)
+ ->setAssignee($this->getUser())
+ ;
+
+ $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not '
+ . 'allowed to create this task');
+
+ $form = $this->createCreateForm($task);
+
+ $form->handleRequest($request);
+
+ if ($form->isSubmitted() && $form->isValid()) {
+ $em = $this->getDoctrine()->getManager();
+ $em->persist($task);
+
+ $this->addFlash('success', "The task is created");
+
+ $em->flush();
+ }
+
+ return $this->render('ChillTaskBundle:SingleTask:new.html.twig', array(
+ 'form' => $form->createView(),
+ 'task' => $task
+ ));
+ }
+
+ /**
+ *
+ * @param SingleTask $task
+ * @return \Symfony\Component\Form\FormInterface
+ */
+ protected function createCreateForm(SingleTask $task)
+ {
+ $form = $this->createForm(SingleTaskType::class, $task, [
+ 'center' => $task->getCenter()
+ ]);
+
+ $form->add('submit', SubmitType::class);
+
+ return $form;
+ }
+
+}
diff --git a/DataFixtures/ORM/LoadTaskACL.php b/DataFixtures/ORM/LoadTaskACL.php
new file mode 100644
index 000000000..f7f88c79c
--- /dev/null
+++ b/DataFixtures/ORM/LoadTaskACL.php
@@ -0,0 +1,86 @@
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+namespace Chill\TaskBundle\DataFixtures\ORM;
+
+use Doctrine\Common\DataFixtures\AbstractFixture;
+use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
+use Doctrine\Common\Persistence\ObjectManager;
+use Chill\MainBundle\DataFixtures\ORM\LoadPermissionsGroup;
+use Chill\MainBundle\Entity\RoleScope;
+use Chill\MainBundle\DataFixtures\ORM\LoadScopes;
+use Chill\TaskBundle\Security\Authorization\TaskVoter;
+
+/**
+ * Add a role UPDATE & CREATE for all groups except administrative,
+ * and a role SEE for administrative
+ *
+ * @author Julien Fastré
+ */
+class LoadTaskACL extends AbstractFixture implements OrderedFixtureInterface
+{
+ public function getOrder()
+ {
+ return 16000;
+ }
+
+
+ public function load(ObjectManager $manager)
+ {
+ foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) {
+ $permissionsGroup = $this->getReference($permissionsGroupRef);
+ foreach (LoadScopes::$references as $scopeRef){
+ $scope = $this->getReference($scopeRef);
+ //create permission group
+ switch ($permissionsGroup->getName()) {
+ case 'social':
+ if ($scope->getName()['en'] === 'administrative') {
+ break 2; // we do not want any power on administrative
+ }
+ break;
+ case 'administrative':
+ case 'direction':
+ if (in_array($scope->getName()['en'], array('administrative', 'social'))) {
+ break 2; // we do not want any power on social or administrative
+ }
+ break;
+ }
+
+ printf("Adding CHILL_TASK_TASK_UPDATE & CHILL_TASK_TASK_CREATE permissions to %s "
+ . "permission group, scope '%s' \n",
+ $permissionsGroup->getName(), $scope->getName()['en']);
+ $roleScopeUpdate = (new RoleScope())
+ ->setRole(TaskVoter::UPDATE)
+ ->setScope($scope);
+ $permissionsGroup->addRoleScope($roleScopeUpdate);
+ $roleScopeCreate = (new RoleScope())
+ ->setRole(TaskVoter::CREATE)
+ ->setScope($scope);
+ $permissionsGroup->addRoleScope($roleScopeCreate);
+
+ $manager->persist($roleScopeUpdate);
+ $manager->persist($roleScopeCreate);
+ }
+
+ }
+
+ $manager->flush();
+ }
+
+}
diff --git a/DependencyInjection/ChillTaskExtension.php b/DependencyInjection/ChillTaskExtension.php
index 0a933e247..2506082da 100644
--- a/DependencyInjection/ChillTaskExtension.php
+++ b/DependencyInjection/ChillTaskExtension.php
@@ -23,6 +23,7 @@ class ChillTaskExtension extends Extension
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
- //$loader->load('services.yml');
+ $loader->load('services/controller.yml');
+ $loader->load('services/security.yml');
}
}
diff --git a/Entity/AbstractTask.php b/Entity/AbstractTask.php
index 3e65be165..4b5df1093 100644
--- a/Entity/AbstractTask.php
+++ b/Entity/AbstractTask.php
@@ -6,13 +6,15 @@ use Doctrine\ORM\Mapping as ORM;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\Person;
use Chill\MainBundle\Entity\Scope;
+use Chill\MainBundle\Entity\HasScopeInterface;
+use Chill\MainBundle\Entity\HasCenterInterface;
/**
* AbstractTask
*
* @ORM\MappedSuperclass()
*/
-abstract class AbstractTask
+abstract class AbstractTask implements HasScopeInterface, HasCenterInterface
{
/**
@@ -27,7 +29,7 @@ abstract class AbstractTask
*
* @ORM\Column(name="current_states", type="json")
*/
- private $currentStates = '';
+ private $currentStates = [];
/**
* @var string
@@ -165,5 +167,55 @@ abstract class AbstractTask
{
return $this->description;
}
+
+ public function getAssignee(): ?User
+ {
+ return $this->assignee;
+ }
+
+ public function getPerson(): ?Person
+ {
+ return $this->person;
+ }
+
+ public function getCircle(): ?Scope
+ {
+ return $this->circle;
+ }
+
+ public function setAssignee(User $assignee)
+ {
+ $this->assignee = $assignee;
+ return $this;
+ }
+
+ public function setPerson(Person $person)
+ {
+ $this->person = $person;
+ return $this;
+ }
+
+ public function setCircle(Scope $circle)
+ {
+ $this->circle = $circle;
+ return $this;
+ }
+
+ public function getCenter(): ?\Chill\MainBundle\Entity\Center
+ {
+ if ($this->getPerson() instanceof Person) {
+ return $this->getPerson()->getCenter();
+ }
+
+ return null;
+
+ }
+
+ public function getScope(): ?\Chill\MainBundle\Entity\Scope
+ {
+ return $this->getCircle();
+ }
+
+
}
diff --git a/Entity/SingleTask.php b/Entity/SingleTask.php
index 81d987b5c..77627906d 100644
--- a/Entity/SingleTask.php
+++ b/Entity/SingleTask.php
@@ -130,9 +130,21 @@ class SingleTask extends AbstractTask
*
* @return \DateInterval
*/
- public function getWarningInterval(): ?\DateInterval
+ public function getWarningInterval()
{
return $this->warningInterval;
}
+
+ function getRecurringTask(): RecurringTask
+ {
+ return $this->recurringTask;
+ }
+
+ function setRecurringTask(RecurringTask $recurringTask)
+ {
+ $this->recurringTask = $recurringTask;
+ }
+
+
}
diff --git a/Form/SingleTaskType.php b/Form/SingleTaskType.php
new file mode 100644
index 000000000..93ed932ba
--- /dev/null
+++ b/Form/SingleTaskType.php
@@ -0,0 +1,74 @@
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+namespace Chill\TaskBundle\Form;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Chill\MainBundle\Form\Type\ChillDateType;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
+use Symfony\Component\Form\Extension\Core\Type\TextareaType;
+use Chill\MainBundle\Form\Type\UserPickerType;
+use Chill\MainBundle\Form\Type\ScopePickerType;
+use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
+use Symfony\Component\Form\Extension\Core\Type\HiddenType;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Security\Core\Role\Role;
+
+/**
+ *
+ *
+ * @author Julien Fastré
+ */
+class SingleTaskType extends AbstractType
+{
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $builder
+ ->add('title', TextType::class)
+ ->add('description', TextareaType::class, [
+ 'required' => false
+ ])
+ ->add('assignee', UserPickerType::class, [
+ 'required' => false,
+ 'center' => $options['center'],
+ 'role' => new Role(\Chill\PersonBundle\Security\Authorization\PersonVoter::UPDATE)
+ ])
+ ->add('circle', ScopePickerType::class, [
+ 'center' => $options['center'],
+ 'role' => new Role(\Chill\ActivityBundle\Security\Authorization\ActivityVoter::SEE)
+ ])
+ ->add('startDate', ChillDateType::class, [
+ 'required' => false
+ ])
+ ->add('endDate', ChillDateType::class, [
+ 'required' => false
+ ])
+ ->add('warningInterval', TextType::class, [
+ 'required' => false
+ ])
+ ;
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver
+ ->setRequired('center')
+ ->setAllowedTypes('center', [ \Chill\MainBundle\Entity\Center::class ])
+ ;
+ }
+}
diff --git a/Resources/config/routing.yml b/Resources/config/routing.yml
index 8b1378917..53d5e4f81 100644
--- a/Resources/config/routing.yml
+++ b/Resources/config/routing.yml
@@ -1 +1,3 @@
-
+chill_task_controllers:
+ resource: "@ChillTaskBundle/Controller"
+ type: annotation
diff --git a/Resources/config/services.yml b/Resources/config/services.yml
new file mode 100644
index 000000000..5779e425e
--- /dev/null
+++ b/Resources/config/services.yml
@@ -0,0 +1,2 @@
+services:
+
diff --git a/Resources/config/services/controller.yml b/Resources/config/services/controller.yml
new file mode 100644
index 000000000..9edb4e9ad
--- /dev/null
+++ b/Resources/config/services/controller.yml
@@ -0,0 +1,4 @@
+services:
+ #chill_task.single_task_controller:
+ # class: Chill\TaskBundle\Controller\SingleTaskController
+ # autowire: true
diff --git a/Resources/config/services/security.yml b/Resources/config/services/security.yml
new file mode 100644
index 000000000..0acc6e56f
--- /dev/null
+++ b/Resources/config/services/security.yml
@@ -0,0 +1,10 @@
+services:
+ chill_task.task_voter:
+ class: Chill\TaskBundle\Security\Authorization\TaskVoter
+ arguments:
+ - "@security.access.decision_manager"
+ - "@chill.main.security.authorization.helper"
+ - "@logger"
+ tags:
+ - { name: security.voter }
+ - { name: chill.role }
diff --git a/Resources/views/SingleTask/new.html.twig b/Resources/views/SingleTask/new.html.twig
new file mode 100644
index 000000000..e656a69fa
--- /dev/null
+++ b/Resources/views/SingleTask/new.html.twig
@@ -0,0 +1,29 @@
+{#
+ * Copyright (C) 2014, Champs Libres Cooperative SCRLFS,
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+#}
+{% extends "ChillPersonBundle::layout.html.twig" %}
+
+{% set activeRouteKey = 'chill_task_single_task_new' %}
+{% set person = task.person %}
+
+{% block title %}{{ 'New task'|trans }}{% endblock %}
+
+{% block personcontent %}
+{{ 'New task'|trans }}
+
+{{ form(form) }}
+
+{% endblock %}
diff --git a/Security/Authorization/TaskVoter.php b/Security/Authorization/TaskVoter.php
new file mode 100644
index 000000000..aacad0dad
--- /dev/null
+++ b/Security/Authorization/TaskVoter.php
@@ -0,0 +1,131 @@
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+namespace Chill\TaskBundle\Security\Authorization;
+
+use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
+use Chill\TaskBundle\Entity\AbstractTask;
+use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
+use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
+use Chill\PersonBundle\Security\Authorization\PersonVoter;
+use Psr\Log\LoggerInterface;
+use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Chill\MainBundle\Entity\User;
+
+/**
+ *
+ *
+ * @author Julien Fastré
+ */
+class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
+{
+ const CREATE = 'CHILL_TASK_TASK_CREATE';
+ const UPDATE = 'CHILL_TASK_TASK_UPDATE';
+ const SHOW = 'CHILL_TASK_TASK_SHOW';
+
+ const ROLES = [
+ self::CREATE,
+ self::UPDATE,
+ self::SHOW
+ ];
+
+ /**
+ *
+ * @var AuthorizationHelper
+ */
+ protected $authorizationHelper;
+
+ /**
+ *
+ * @var AccessDecisionManagerInterface
+ */
+ protected $accessDecisionManager;
+
+ /**
+ *
+ * @var LoggerInterface
+ */
+ protected $logger;
+
+ public function __construct(
+ AccessDecisionManagerInterface $accessDecisionManager,
+ AuthorizationHelper $authorizationHelper,
+ LoggerInterface $logger
+ ) {
+ $this->accessDecisionManager = $accessDecisionManager;
+ $this->authorizationHelper = $authorizationHelper;
+ $this->logger = $logger;
+ }
+
+ public function supports($attribute, $subject)
+ {
+ return $subject instanceof AbstractTask
+ && in_array($attribute, self::ROLES);
+ }
+
+ /**
+ *
+ * @param string $attribute
+ * @param AbstractTask $subject
+ * @param TokenInterface $token
+ * @return boolean
+ */
+ protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
+ {
+ $this->logger->debug(sprintf("Voting from %s class", self::class));
+
+ if (!$token->getUser() instanceof User) {
+ return false;
+ }
+
+ if ($subject->getPerson() === null) {
+ throw new \LogicException("You should associate a person with task "
+ . "in order to check autorizations");
+ }
+
+ if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $subject->getPerson())) {
+
+ return false;
+ }
+
+ return $this->authorizationHelper->userHasAccess(
+ $token->getUser(),
+ $subject,
+ $attribute
+ );
+ }
+
+ public function getRoles()
+ {
+ return self::ROLES;
+ }
+
+ public function getRolesWithHierarchy(): array
+ {
+ return [
+ 'Task' => self::ROLES
+ ];
+ }
+
+ public function getRolesWithoutScope()
+ {
+ return [];
+ }
+
+}