first implementation of controller

This commit is contained in:
Julien Fastré 2018-04-16 15:09:50 +02:00
parent 1734727029
commit a02b9edc45
13 changed files with 509 additions and 18 deletions

View File

@ -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');
}
}

View File

@ -0,0 +1,101 @@
<?php
namespace Chill\TaskBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Doctrine\ORM\EntityManager;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Chill\TaskBundle\Entity\SingleTask;
use Chill\TaskBundle\Form\SingleTaskType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormFactoryInterface;
use Chill\TaskBundle\Security\Authorization\TaskVoter;
class SingleTaskController extends Controller
{
/**
*
* @var EntityManager
*/
protected $em;
/**
*
* @var FormFactoryInterface
*/
protected $formFactory;
/*public function __construct(
EntityManager $em,
FormFactoryInterface $formFactory)
{
$this->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;
}
}

View File

@ -0,0 +1,86 @@
<?php
/*
* Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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é <julien.fastre@champs-libres.coop>
*/
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();
}
}

View File

@ -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');
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

74
Form/SingleTaskType.php Normal file
View File

@ -0,0 +1,74 @@
<?php
/*
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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é <julien.fastre@champs-libres.coop>
*/
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 ])
;
}
}

View File

@ -1 +1,3 @@
chill_task_controllers:
resource: "@ChillTaskBundle/Controller"
type: annotation

View File

@ -0,0 +1,2 @@
services:

View File

@ -0,0 +1,4 @@
services:
#chill_task.single_task_controller:
# class: Chill\TaskBundle\Controller\SingleTaskController
# autowire: true

View File

@ -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 }

View File

@ -0,0 +1,29 @@
{#
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
#}
{% extends "ChillPersonBundle::layout.html.twig" %}
{% set activeRouteKey = 'chill_task_single_task_new' %}
{% set person = task.person %}
{% block title %}{{ 'New task'|trans }}{% endblock %}
{% block personcontent %}
<h1>{{ 'New task'|trans }}</h1>
{{ form(form) }}
{% endblock %}

View File

@ -0,0 +1,131 @@
<?php
/*
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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é <julien.fastre@champs-libres.coop>
*/
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 [];
}
}