mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-30 03:23:48 +00:00
cs: Fix code style (safe rules only).
This commit is contained in:
@@ -1,36 +1,24 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Chill\TaskBundle\DependencyInjection\Compiler\TaskWorkflowDefinitionCompilerPass;
|
||||
|
||||
/**
|
||||
*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle;
|
||||
|
||||
use Chill\TaskBundle\DependencyInjection\Compiler\TaskWorkflowDefinitionCompilerPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
class ChillTaskBundle extends Bundle
|
||||
{
|
||||
public function build(ContainerBuilder $container)
|
||||
{
|
||||
parent::build($container);
|
||||
|
||||
|
||||
$container->addCompilerPass(new TaskWorkflowDefinitionCompilerPass());
|
||||
}
|
||||
}
|
||||
|
@@ -1,47 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\TaskBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
||||
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
|
||||
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
|
||||
use Chill\PersonBundle\Privacy\PrivacyEvent;
|
||||
use Chill\TaskBundle\Repository\SingleTaskAclAwareRepositoryInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
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;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\TaskBundle\Event\TaskEvent;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Chill\TaskBundle\Event\UI\UIEvent;
|
||||
use Chill\MainBundle\Timeline\TimelineBuilder;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Privacy\PrivacyEvent;
|
||||
use Chill\TaskBundle\Entity\SingleTask;
|
||||
use Chill\TaskBundle\Event\TaskEvent;
|
||||
use Chill\TaskBundle\Event\UI\UIEvent;
|
||||
use Chill\TaskBundle\Form\SingleTaskType;
|
||||
use Chill\TaskBundle\Repository\SingleTaskAclAwareRepositoryInterface;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use LogicException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface as TranslationTranslatorInterface;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
|
||||
final class SingleTaskController extends AbstractController
|
||||
{
|
||||
private EventDispatcherInterface $eventDispatcher;
|
||||
private TimelineBuilder $timelineBuilder;
|
||||
private LoggerInterface $logger;
|
||||
private CenterResolverDispatcherInterface $centerResolverDispatcher;
|
||||
private TranslatorInterface $translator;
|
||||
private PaginatorFactory $paginatorFactory;
|
||||
private SingleTaskAclAwareRepositoryInterface $singleTaskAclAwareRepository;
|
||||
|
||||
private EventDispatcherInterface $eventDispatcher;
|
||||
|
||||
private FilterOrderHelperFactoryInterface $filterOrderHelperFactory;
|
||||
|
||||
private LoggerInterface $logger;
|
||||
|
||||
private PaginatorFactory $paginatorFactory;
|
||||
|
||||
private SingleTaskAclAwareRepositoryInterface $singleTaskAclAwareRepository;
|
||||
|
||||
private TimelineBuilder $timelineBuilder;
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
public function __construct(
|
||||
CenterResolverDispatcherInterface $centerResolverDispatcher,
|
||||
PaginatorFactory $paginatorFactory,
|
||||
@@ -62,261 +78,16 @@ final class SingleTaskController extends AbstractController
|
||||
$this->filterOrderHelperFactory = $filterOrderHelperFactory;
|
||||
}
|
||||
|
||||
private function getEntityContext(Request $request)
|
||||
/**
|
||||
* @Route(
|
||||
* "/{_locale}/task/single-task/{id}/delete",
|
||||
* name="chill_task_single_task_delete"
|
||||
* )
|
||||
*
|
||||
* @param mixed $id
|
||||
*/
|
||||
public function deleteAction(Request $request, $id)
|
||||
{
|
||||
if ($request->query->has('person_id')) {
|
||||
return 'person';
|
||||
} else if ($request->query->has('course_id')) {
|
||||
return 'course';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Route(
|
||||
* "/{_locale}/task/single-task/new",
|
||||
* name="chill_task_single_task_new"
|
||||
* )
|
||||
*/
|
||||
public function newAction(Request $request) {
|
||||
|
||||
$task = (new SingleTask())
|
||||
->setAssignee($this->getUser())
|
||||
->setType('task_default')
|
||||
;
|
||||
|
||||
$entityType = $this->getEntityContext($request);
|
||||
|
||||
if (NULL === $entityType) {
|
||||
throw new BadRequestHttpException("You must provide a entity_type");
|
||||
}
|
||||
|
||||
$entityId = $request->query->getInt("{$entityType}_id", 0);
|
||||
|
||||
if ($entityId === null) {
|
||||
return new BadRequestHttpException("You must provide a {$entityType}_id");
|
||||
}
|
||||
|
||||
switch ($entityType) {
|
||||
case 'person':
|
||||
$person = $this->getDoctrine()->getManager()
|
||||
->getRepository(Person::class)
|
||||
->find($entityId);
|
||||
|
||||
if ($person === null) {
|
||||
$this->createNotFoundException("Invalid person id");
|
||||
}
|
||||
|
||||
$task->setPerson($person);
|
||||
$role = TaskVoter::CREATE_PERSON;
|
||||
break;
|
||||
case 'course':
|
||||
$course = $this->getDoctrine()->getManager()
|
||||
->getRepository(AccompanyingPeriod::class)
|
||||
->find($entityId);
|
||||
|
||||
if ($course === null) {
|
||||
$this->createNotFoundException("Invalid accompanying course id");
|
||||
}
|
||||
|
||||
$task->setCourse($course);
|
||||
$role = TaskVoter::CREATE_COURSE;
|
||||
break;
|
||||
default:
|
||||
return new BadRequestHttpException("context with {$entityType} is not supported");
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted($role, $task, 'You are not '
|
||||
. 'allowed to create this task');
|
||||
|
||||
$form = $this->setCreateForm($task, new Role($role));
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted()) {
|
||||
if ($form->isValid()) {
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->persist($task);
|
||||
|
||||
$this->eventDispatcher->dispatch(TaskEvent::PERSIST, new TaskEvent($task));
|
||||
|
||||
$em->flush();
|
||||
|
||||
$this->addFlash('success', $this->translator->trans("The task is created"));
|
||||
|
||||
if ($request->query->has('returnPath')) {
|
||||
return $this->redirect($request->query->get('returnPath'));
|
||||
}
|
||||
|
||||
if ($entityType === 'person') {
|
||||
return $this->redirectToRoute('chill_task_singletask_by-person_list', [
|
||||
'id' => $task->getPerson()->getId()
|
||||
]);
|
||||
} elseif ($entityType === 'course') {
|
||||
return $this->redirectToRoute('chill_task_singletask_by-course_list', [
|
||||
'id' => $task->getCourse()->getId()
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$this->addFlash('error', $this->translator->trans("This form contains errors"));
|
||||
}
|
||||
}
|
||||
|
||||
switch ($entityType) {
|
||||
case 'person':
|
||||
return $this->render('@ChillTask/SingleTask/Person/new.html.twig', array(
|
||||
'form' => $form->createView(),
|
||||
'task' => $task,
|
||||
'person' => $task->getPerson(),
|
||||
));
|
||||
case 'course':
|
||||
return $this->render('@ChillTask/SingleTask/AccompanyingCourse/new.html.twig', array(
|
||||
'form' => $form->createView(),
|
||||
'task' => $task,
|
||||
'accompanyingCourse' => $task->getCourse(),
|
||||
));
|
||||
default:
|
||||
throw new \LogicException("entity context not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(
|
||||
* "/{_locale}/task/single-task/{id}/show",
|
||||
* name="chill_task_single_task_show"
|
||||
* )
|
||||
*/
|
||||
public function showAction(SingleTask $task, Request $request)
|
||||
{
|
||||
$this->denyAccessUnlessGranted(TaskVoter::SHOW, $task);
|
||||
|
||||
if ($person = $task->getContext() instanceof Person) {
|
||||
$event = new PrivacyEvent($person, array(
|
||||
'element_class' => SingleTask::class,
|
||||
'element_id' => $task->getId(),
|
||||
'action' => 'show'
|
||||
));
|
||||
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||
}
|
||||
|
||||
$timeline = $this->timelineBuilder
|
||||
->getTimelineHTML('task', array('task' => $task));
|
||||
|
||||
if ($task->getContext() instanceof Person) {
|
||||
return $this->render('@ChillTask/SingleTask/Person/show.html.twig', array(
|
||||
'task' => $task,
|
||||
'timeline' => $timeline
|
||||
));
|
||||
} else {
|
||||
return $this->render('@ChillTask/SingleTask/AccompanyingCourse/show.html.twig', array(
|
||||
'task' => $task,
|
||||
'timeline' => $timeline
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Route(
|
||||
* "/{_locale}/task/single-task/{id}/edit",
|
||||
* name="chill_task_single_task_edit"
|
||||
* )
|
||||
*/
|
||||
public function editAction(
|
||||
SingleTask $task,
|
||||
Request $request
|
||||
) {
|
||||
|
||||
$this->denyAccessUnlessGranted(TaskVoter::UPDATE, $task, 'You are not '
|
||||
. 'allowed to edit this task');
|
||||
|
||||
$event = (new UIEvent('single-task', $task))
|
||||
->setForm($this->setCreateForm($task, new Role(TaskVoter::UPDATE)))
|
||||
;
|
||||
$this->eventDispatcher->dispatch(UIEvent::EDIT_FORM, $event);
|
||||
|
||||
$form = $event->getForm();
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted()) {
|
||||
if ($form->isValid()) {
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->persist($task);
|
||||
|
||||
$em->flush();
|
||||
|
||||
$this->addFlash('success', $this->translator
|
||||
->trans("The task has been updated"));
|
||||
|
||||
if ($task->getContext() instanceof Person) {
|
||||
$event = new PrivacyEvent($task->getPerson(), array(
|
||||
'element_class' => SingleTask::class,
|
||||
'element_id' => $task->getId(),
|
||||
'action' => 'update'
|
||||
));
|
||||
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||
|
||||
if ($request->query->has('returnPath')) {
|
||||
return $this->redirect($request->query->get('returnPath'));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'chill_task_singletask_list',
|
||||
);
|
||||
} else {
|
||||
if ($request->query->has('returnPath')) {
|
||||
return $this->redirect($request->query->get('returnPath'));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'chill_task_singletask_by-course_list', ['id' => $task->getCourse()->getId()]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$this->addFlash('error', $this->translator->trans("This form contains errors"));
|
||||
}
|
||||
}
|
||||
|
||||
$this->eventDispatcher->dispatch(UIEvent::EDIT_PAGE, $event);
|
||||
|
||||
if ($event->hasResponse()) {
|
||||
return $event->getResponse();
|
||||
}
|
||||
|
||||
if ($task->getContext() instanceof Person) {
|
||||
$event = new PrivacyEvent($task->getPerson(), array(
|
||||
'element_class' => SingleTask::class,
|
||||
'element_id' => $task->getId(),
|
||||
'action' => 'edit'
|
||||
));
|
||||
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||
|
||||
return $this->render('@ChillTask/SingleTask/Person/edit.html.twig', array(
|
||||
'task' => $task,
|
||||
'form' => $form->createView()
|
||||
));
|
||||
} else {
|
||||
return $this->render('@ChillTask/SingleTask/AccompanyingCourse/edit.html.twig', array(
|
||||
'task' => $task,
|
||||
'form' => $form->createView(),
|
||||
'accompanyingCourse' => $task->getCourse()
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Route(
|
||||
* "/{_locale}/task/single-task/{id}/delete",
|
||||
* name="chill_task_single_task_delete"
|
||||
* )
|
||||
*/
|
||||
public function deleteAction(Request $request, $id) {
|
||||
$course = null;
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$task = $em->getRepository(SingleTask::class)->find($id);
|
||||
@@ -327,30 +98,31 @@ final class SingleTaskController extends AbstractController
|
||||
|
||||
if ($task->getPerson() !== null) {
|
||||
$personId = $task->getPerson()->getId();
|
||||
if ($personId === null) {
|
||||
return new Response("You must provide a person_id", Response::HTTP_BAD_REQUEST);
|
||||
|
||||
if (null === $personId) {
|
||||
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");
|
||||
if (null === $person) {
|
||||
throw $this->createNotFoundException('Invalid person id');
|
||||
}
|
||||
|
||||
} else {
|
||||
$courseId = $task->getCourse()->getId();
|
||||
if ($courseId === null){
|
||||
return new Response("You must provide a course_id", Response::HTTP_BAD_REQUEST);
|
||||
|
||||
if (null === $courseId) {
|
||||
return new Response('You must provide a course_id', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$course = $this->getDoctrine()->getManager()
|
||||
->getRepository(AccompanyingPeriod::class)
|
||||
->find($courseId);
|
||||
|
||||
if($course === null){
|
||||
throw $this->createNotFoundException("Invalid accompanying period id");
|
||||
if (null === $course) {
|
||||
throw $this->createNotFoundException('Invalid accompanying period id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,34 +137,33 @@ final class SingleTaskController extends AbstractController
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
|
||||
$this->logger->notice("A task has been removed", array(
|
||||
'by_user' => $this->getUser()->getUsername(),
|
||||
'task_id' => $task->getId(),
|
||||
'description' => $task->getDescription(),
|
||||
'assignee' => $task->getAssignee(),
|
||||
// TODO reimplement scope
|
||||
// 'scope_id' => $task->getScope()->getId(),
|
||||
));
|
||||
$this->logger->notice('A task has been removed', [
|
||||
'by_user' => $this->getUser()->getUsername(),
|
||||
'task_id' => $task->getId(),
|
||||
'description' => $task->getDescription(),
|
||||
'assignee' => $task->getAssignee(),
|
||||
// TODO reimplement scope
|
||||
// 'scope_id' => $task->getScope()->getId(),
|
||||
]);
|
||||
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->remove($task);
|
||||
$em->flush();
|
||||
|
||||
$this->addFlash('success', $this->translator
|
||||
->trans("The task has been successfully removed."));
|
||||
->trans('The task has been successfully removed.'));
|
||||
|
||||
if ($task->getContext() instanceof Person) {
|
||||
return $this->redirect($this->generateUrl(
|
||||
'chill_task_singletask_by-person_list',
|
||||
[ 'id' => $task->getPerson()->getId() ]
|
||||
));
|
||||
} else {
|
||||
return $this->redirect($this->generateUrl(
|
||||
'chill_task_singletask_by-course_list',
|
||||
['id' => $task->getCourse()->getId()]
|
||||
));
|
||||
['id' => $task->getPerson()->getId()]
|
||||
));
|
||||
}
|
||||
|
||||
return $this->redirect($this->generateUrl(
|
||||
'chill_task_singletask_by-course_list',
|
||||
['id' => $task->getCourse()->getId()]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,102 +182,110 @@ final class SingleTaskController extends AbstractController
|
||||
[
|
||||
'task' => $task,
|
||||
'delete_form' => $form->createView(),
|
||||
'accompanyingCourse' => $course
|
||||
'accompanyingCourse' => $course,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param SingleTask $task
|
||||
* @param Role $role
|
||||
* @return \Symfony\Component\Form\FormInterface
|
||||
*/
|
||||
protected function setCreateForm(SingleTask $task, Role $role)
|
||||
{
|
||||
$form = $this->createForm(SingleTaskType::class, $task, [
|
||||
'role' => $role,
|
||||
]);
|
||||
|
||||
$form->add('submit', SubmitType::class);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Response
|
||||
* @Route(
|
||||
* "/{_locale}/task/single-task/list/my",
|
||||
* name="chill_task_singletask_my_tasks"
|
||||
* "/{_locale}/task/single-task/{id}/edit",
|
||||
* name="chill_task_single_task_edit"
|
||||
* )
|
||||
*/
|
||||
public function myTasksAction()
|
||||
{
|
||||
$this->denyAccessUnlessGranted('ROLE_USER');
|
||||
public function editAction(
|
||||
SingleTask $task,
|
||||
Request $request
|
||||
) {
|
||||
$this->denyAccessUnlessGranted(TaskVoter::UPDATE, $task, 'You are not '
|
||||
. 'allowed to edit this task');
|
||||
|
||||
$filterOrder = $this->buildFilterOrder();
|
||||
$flags = \array_merge(
|
||||
$filterOrder->getCheckboxData('status'),
|
||||
\array_map(fn ($i) => 'state_'.$i, $filterOrder->getCheckboxData('states'))
|
||||
);
|
||||
$nb = $this->singleTaskAclAwareRepository->countByCurrentUsersTasks(
|
||||
$filterOrder->getQueryString(),
|
||||
$flags
|
||||
);
|
||||
$paginator = $this->paginatorFactory->create($nb);
|
||||
$tasks = $this->singleTaskAclAwareRepository->findByCurrentUsersTasks(
|
||||
$filterOrder->getQueryString(),
|
||||
$flags,
|
||||
$paginator->getCurrentPageFirstItemNumber(),
|
||||
$paginator->getItemsPerPage(),
|
||||
[
|
||||
'startDate' => 'DESC',
|
||||
'endDate' => 'DESC',
|
||||
]
|
||||
);
|
||||
$event = (new UIEvent('single-task', $task))
|
||||
->setForm($this->setCreateForm($task, new Role(TaskVoter::UPDATE)));
|
||||
$this->eventDispatcher->dispatch(UIEvent::EDIT_FORM, $event);
|
||||
|
||||
return $this->render('@ChillTask/SingleTask/List/index_my_tasks.html.twig', [
|
||||
'tasks' => $tasks,
|
||||
'paginator' => $paginator,
|
||||
'filter_order' => $filterOrder,
|
||||
$form = $event->getForm();
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted()) {
|
||||
if ($form->isValid()) {
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->persist($task);
|
||||
|
||||
$em->flush();
|
||||
|
||||
$this->addFlash('success', $this->translator
|
||||
->trans('The task has been updated'));
|
||||
|
||||
if ($task->getContext() instanceof Person) {
|
||||
$event = new PrivacyEvent($task->getPerson(), [
|
||||
'element_class' => SingleTask::class,
|
||||
'element_id' => $task->getId(),
|
||||
'action' => 'update',
|
||||
]);
|
||||
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||
|
||||
if ($request->query->has('returnPath')) {
|
||||
return $this->redirect($request->query->get('returnPath'));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'chill_task_singletask_list',
|
||||
);
|
||||
}
|
||||
|
||||
if ($request->query->has('returnPath')) {
|
||||
return $this->redirect($request->query->get('returnPath'));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'chill_task_singletask_by-course_list',
|
||||
['id' => $task->getCourse()->getId()]
|
||||
);
|
||||
}
|
||||
$this->addFlash('error', $this->translator->trans('This form contains errors'));
|
||||
}
|
||||
|
||||
$this->eventDispatcher->dispatch(UIEvent::EDIT_PAGE, $event);
|
||||
|
||||
if ($event->hasResponse()) {
|
||||
return $event->getResponse();
|
||||
}
|
||||
|
||||
if ($task->getContext() instanceof Person) {
|
||||
$event = new PrivacyEvent($task->getPerson(), [
|
||||
'element_class' => SingleTask::class,
|
||||
'element_id' => $task->getId(),
|
||||
'action' => 'edit',
|
||||
]);
|
||||
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||
|
||||
return $this->render('@ChillTask/SingleTask/Person/edit.html.twig', [
|
||||
'task' => $task,
|
||||
'form' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render('@ChillTask/SingleTask/AccompanyingCourse/edit.html.twig', [
|
||||
'task' => $task,
|
||||
'form' => $form->createView(),
|
||||
'accompanyingCourse' => $task->getCourse(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function buildFilterOrder(): FilterOrderHelper
|
||||
{
|
||||
$statuses = ['no-alert', 'warning', 'alert'];
|
||||
$statusTrans = [
|
||||
'Tasks without alert',
|
||||
'Tasks near deadline',
|
||||
'Tasks over deadline',
|
||||
];
|
||||
$states = [
|
||||
// todo: get a list of possible states dynamically
|
||||
'new', 'in_progress', 'closed', 'canceled'
|
||||
];
|
||||
return $this->filterOrderHelperFactory
|
||||
->create(self::class)
|
||||
->addSearchBox()
|
||||
->addCheckbox('status', $statuses, $statuses, $statusTrans)
|
||||
->addCheckbox('states', $states, ['new', 'in_progress'])
|
||||
->build()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Arguments:
|
||||
* - user_id
|
||||
* - scope_id
|
||||
* - s
|
||||
* - person_id
|
||||
* - hide_form (hide the form to filter the tasks)
|
||||
* - status: date state, amongst SingleTaskRepository::DATE_STATUSES, or 'closed'
|
||||
* - status: date state, amongst SingleTaskRepository::DATE_STATUSES, or 'closed'.
|
||||
*
|
||||
* @Route(
|
||||
* "/{_locale}/task/single-task/list",
|
||||
* name="chill_task_singletask_list"
|
||||
* "/{_locale}/task/single-task/list",
|
||||
* name="chill_task_singletask_list"
|
||||
* )
|
||||
*/
|
||||
public function listAction(
|
||||
@@ -515,9 +294,9 @@ final class SingleTaskController extends AbstractController
|
||||
$this->denyAccessUnlessGranted(TaskVoter::SHOW, null);
|
||||
|
||||
$filterOrder = $this->buildFilterOrder();
|
||||
$flags = \array_merge(
|
||||
$flags = array_merge(
|
||||
$filterOrder->getCheckboxData('status'),
|
||||
\array_map(fn ($i) => 'state_'.$i, $filterOrder->getCheckboxData('states'))
|
||||
array_map(fn ($i) => 'state_' . $i, $filterOrder->getCheckboxData('states'))
|
||||
);
|
||||
$nb = $this->singleTaskAclAwareRepository->countByAllViewable(
|
||||
$filterOrder->getQueryString(),
|
||||
@@ -541,49 +320,28 @@ final class SingleTaskController extends AbstractController
|
||||
}
|
||||
|
||||
return $this->render('@ChillTask/SingleTask/List/index.html.twig', [
|
||||
'tasks' => $tasks,
|
||||
'paginator' => $paginator,
|
||||
'filter_order' => $filterOrder
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a form to delete a Task entity by id.
|
||||
*
|
||||
* @param mixed $id The entity id
|
||||
*
|
||||
* @return \Symfony\Component\Form\Form The form
|
||||
*/
|
||||
private function createDeleteForm($id)
|
||||
{
|
||||
return $this->createFormBuilder()
|
||||
->setAction($this->generateUrl(
|
||||
'chill_task_single_task_delete',
|
||||
array('id' => $id)))
|
||||
->setMethod('DELETE')
|
||||
->add('submit', SubmitType::class, array('label' => 'Delete'))
|
||||
->getForm()
|
||||
;
|
||||
'tasks' => $tasks,
|
||||
'paginator' => $paginator,
|
||||
'filter_order' => $filterOrder,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(
|
||||
* "/{_locale}/task/single-task/by-course/{id}",
|
||||
* name="chill_task_singletask_by-course_list")
|
||||
* "/{_locale}/task/single-task/by-course/{id}",
|
||||
* name="chill_task_singletask_by-course_list")
|
||||
*/
|
||||
public function listCourseTasks(
|
||||
AccompanyingPeriod $course,
|
||||
FormFactoryInterface $formFactory,
|
||||
Request $request
|
||||
): Response
|
||||
{
|
||||
): Response {
|
||||
$this->denyAccessUnlessGranted(TaskVoter::SHOW, $course);
|
||||
|
||||
$filterOrder = $this->buildFilterOrder();
|
||||
$flags = \array_merge(
|
||||
$flags = array_merge(
|
||||
$filterOrder->getCheckboxData('status'),
|
||||
\array_map(fn ($i) => 'state_'.$i, $filterOrder->getCheckboxData('states'))
|
||||
array_map(fn ($i) => 'state_' . $i, $filterOrder->getCheckboxData('states'))
|
||||
);
|
||||
$nb = $this->singleTaskAclAwareRepository->countByCourse(
|
||||
$course,
|
||||
@@ -614,25 +372,25 @@ final class SingleTaskController extends AbstractController
|
||||
'tasks' => $tasks,
|
||||
'accompanyingCourse' => $course,
|
||||
'paginator' => $paginator,
|
||||
'filter_order' => $filterOrder
|
||||
]);
|
||||
'filter_order' => $filterOrder,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(
|
||||
* "/{_locale}/task/single-task/by-person/{id}",
|
||||
* name="chill_task_singletask_by-person_list")
|
||||
* "/{_locale}/task/single-task/by-person/{id}",
|
||||
* name="chill_task_singletask_by-person_list")
|
||||
*/
|
||||
public function listPersonTasks(
|
||||
Person $person
|
||||
): Response {
|
||||
|
||||
$this->denyAccessUnlessGranted(TaskVoter::SHOW, $person);
|
||||
|
||||
$filterOrder = $this->buildFilterOrder();
|
||||
$flags = \array_merge(
|
||||
$flags = array_merge(
|
||||
$filterOrder->getCheckboxData('status'),
|
||||
\array_map(fn ($i) => 'state_'.$i, $filterOrder->getCheckboxData('states'))
|
||||
array_map(fn ($i) => 'state_' . $i, $filterOrder->getCheckboxData('states'))
|
||||
);
|
||||
$nb = $this->singleTaskAclAwareRepository->countByPerson(
|
||||
$person,
|
||||
@@ -663,7 +421,264 @@ final class SingleTaskController extends AbstractController
|
||||
'tasks' => $tasks,
|
||||
'person' => $person,
|
||||
'paginator' => $paginator,
|
||||
'filter_order' => $filterOrder
|
||||
'filter_order' => $filterOrder,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response
|
||||
* @Route(
|
||||
* "/{_locale}/task/single-task/list/my",
|
||||
* name="chill_task_singletask_my_tasks"
|
||||
* )
|
||||
*/
|
||||
public function myTasksAction()
|
||||
{
|
||||
$this->denyAccessUnlessGranted('ROLE_USER');
|
||||
|
||||
$filterOrder = $this->buildFilterOrder();
|
||||
$flags = array_merge(
|
||||
$filterOrder->getCheckboxData('status'),
|
||||
array_map(fn ($i) => 'state_' . $i, $filterOrder->getCheckboxData('states'))
|
||||
);
|
||||
$nb = $this->singleTaskAclAwareRepository->countByCurrentUsersTasks(
|
||||
$filterOrder->getQueryString(),
|
||||
$flags
|
||||
);
|
||||
$paginator = $this->paginatorFactory->create($nb);
|
||||
$tasks = $this->singleTaskAclAwareRepository->findByCurrentUsersTasks(
|
||||
$filterOrder->getQueryString(),
|
||||
$flags,
|
||||
$paginator->getCurrentPageFirstItemNumber(),
|
||||
$paginator->getItemsPerPage(),
|
||||
[
|
||||
'startDate' => 'DESC',
|
||||
'endDate' => 'DESC',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->render('@ChillTask/SingleTask/List/index_my_tasks.html.twig', [
|
||||
'tasks' => $tasks,
|
||||
'paginator' => $paginator,
|
||||
'filter_order' => $filterOrder,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(
|
||||
* "/{_locale}/task/single-task/new",
|
||||
* name="chill_task_single_task_new"
|
||||
* )
|
||||
*/
|
||||
public function newAction(Request $request)
|
||||
{
|
||||
$task = (new SingleTask())
|
||||
->setAssignee($this->getUser())
|
||||
->setType('task_default');
|
||||
|
||||
$entityType = $this->getEntityContext($request);
|
||||
|
||||
if (null === $entityType) {
|
||||
throw new BadRequestHttpException('You must provide a entity_type');
|
||||
}
|
||||
|
||||
$entityId = $request->query->getInt("{$entityType}_id", 0);
|
||||
|
||||
if (null === $entityId) {
|
||||
return new BadRequestHttpException("You must provide a {$entityType}_id");
|
||||
}
|
||||
|
||||
switch ($entityType) {
|
||||
case 'person':
|
||||
$person = $this->getDoctrine()->getManager()
|
||||
->getRepository(Person::class)
|
||||
->find($entityId);
|
||||
|
||||
if (null === $person) {
|
||||
$this->createNotFoundException('Invalid person id');
|
||||
}
|
||||
|
||||
$task->setPerson($person);
|
||||
$role = TaskVoter::CREATE_PERSON;
|
||||
|
||||
break;
|
||||
|
||||
case 'course':
|
||||
$course = $this->getDoctrine()->getManager()
|
||||
->getRepository(AccompanyingPeriod::class)
|
||||
->find($entityId);
|
||||
|
||||
if (null === $course) {
|
||||
$this->createNotFoundException('Invalid accompanying course id');
|
||||
}
|
||||
|
||||
$task->setCourse($course);
|
||||
$role = TaskVoter::CREATE_COURSE;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return new BadRequestHttpException("context with {$entityType} is not supported");
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted($role, $task, 'You are not '
|
||||
. 'allowed to create this task');
|
||||
|
||||
$form = $this->setCreateForm($task, new Role($role));
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted()) {
|
||||
if ($form->isValid()) {
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->persist($task);
|
||||
|
||||
$this->eventDispatcher->dispatch(TaskEvent::PERSIST, new TaskEvent($task));
|
||||
|
||||
$em->flush();
|
||||
|
||||
$this->addFlash('success', $this->translator->trans('The task is created'));
|
||||
|
||||
if ($request->query->has('returnPath')) {
|
||||
return $this->redirect($request->query->get('returnPath'));
|
||||
}
|
||||
|
||||
if ('person' === $entityType) {
|
||||
return $this->redirectToRoute('chill_task_singletask_by-person_list', [
|
||||
'id' => $task->getPerson()->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
if ('course' === $entityType) {
|
||||
return $this->redirectToRoute('chill_task_singletask_by-course_list', [
|
||||
'id' => $task->getCourse()->getId(),
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$this->addFlash('error', $this->translator->trans('This form contains errors'));
|
||||
}
|
||||
}
|
||||
|
||||
switch ($entityType) {
|
||||
case 'person':
|
||||
return $this->render('@ChillTask/SingleTask/Person/new.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'task' => $task,
|
||||
'person' => $task->getPerson(),
|
||||
]);
|
||||
|
||||
case 'course':
|
||||
return $this->render('@ChillTask/SingleTask/AccompanyingCourse/new.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'task' => $task,
|
||||
'accompanyingCourse' => $task->getCourse(),
|
||||
]);
|
||||
|
||||
default:
|
||||
throw new LogicException('entity context not supported');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(
|
||||
* "/{_locale}/task/single-task/{id}/show",
|
||||
* name="chill_task_single_task_show"
|
||||
* )
|
||||
*/
|
||||
public function showAction(SingleTask $task, Request $request)
|
||||
{
|
||||
$this->denyAccessUnlessGranted(TaskVoter::SHOW, $task);
|
||||
|
||||
if ($person = $task->getContext() instanceof Person) {
|
||||
$event = new PrivacyEvent($person, [
|
||||
'element_class' => SingleTask::class,
|
||||
'element_id' => $task->getId(),
|
||||
'action' => 'show',
|
||||
]);
|
||||
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
|
||||
}
|
||||
|
||||
$timeline = $this->timelineBuilder
|
||||
->getTimelineHTML('task', ['task' => $task]);
|
||||
|
||||
if ($task->getContext() instanceof Person) {
|
||||
return $this->render('@ChillTask/SingleTask/Person/show.html.twig', [
|
||||
'task' => $task,
|
||||
'timeline' => $timeline,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render('@ChillTask/SingleTask/AccompanyingCourse/show.html.twig', [
|
||||
'task' => $task,
|
||||
'timeline' => $timeline,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Symfony\Component\Form\FormInterface
|
||||
*/
|
||||
protected function setCreateForm(SingleTask $task, Role $role)
|
||||
{
|
||||
$form = $this->createForm(SingleTaskType::class, $task, [
|
||||
'role' => $role,
|
||||
]);
|
||||
|
||||
$form->add('submit', SubmitType::class);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
private function buildFilterOrder(): FilterOrderHelper
|
||||
{
|
||||
$statuses = ['no-alert', 'warning', 'alert'];
|
||||
$statusTrans = [
|
||||
'Tasks without alert',
|
||||
'Tasks near deadline',
|
||||
'Tasks over deadline',
|
||||
];
|
||||
$states = [
|
||||
// todo: get a list of possible states dynamically
|
||||
'new', 'in_progress', 'closed', 'canceled',
|
||||
];
|
||||
|
||||
return $this->filterOrderHelperFactory
|
||||
->create(self::class)
|
||||
->addSearchBox()
|
||||
->addCheckbox('status', $statuses, $statuses, $statusTrans)
|
||||
->addCheckbox('states', $states, ['new', 'in_progress'])
|
||||
->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a form to delete a Task entity by id.
|
||||
*
|
||||
* @param mixed $id The entity id
|
||||
*
|
||||
* @return \Symfony\Component\Form\Form The form
|
||||
*/
|
||||
private function createDeleteForm($id)
|
||||
{
|
||||
return $this->createFormBuilder()
|
||||
->setAction($this->generateUrl(
|
||||
'chill_task_single_task_delete',
|
||||
['id' => $id]
|
||||
))
|
||||
->setMethod('DELETE')
|
||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||
->getForm();
|
||||
}
|
||||
|
||||
private function getEntityContext(Request $request)
|
||||
{
|
||||
if ($request->query->has('person_id')) {
|
||||
return 'person';
|
||||
}
|
||||
|
||||
if ($request->query->has('course_id')) {
|
||||
return 'course';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -1,45 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Chill\TaskBundle\Entity\AbstractTask;
|
||||
use Chill\TaskBundle\Event\UI\UIEvent;
|
||||
use Chill\TaskBundle\Repository\SingleTaskRepository;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Chill\TaskBundle\Event\UI\UIEvent;
|
||||
use Chill\TaskBundle\Entity\AbstractTask;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
use Symfony\Component\Workflow\Transition;
|
||||
use function array_filter;
|
||||
use function array_values;
|
||||
|
||||
/**
|
||||
* Class TaskController
|
||||
*
|
||||
* @package Chill\TaskBundle\Controller
|
||||
* Class TaskController.
|
||||
*/
|
||||
class TaskController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* Apply a transition to a task
|
||||
* Apply a transition to a task.
|
||||
*
|
||||
* @Route(
|
||||
* "/{_locale}/task/transition/{kind}/{taskId}/{transition}",
|
||||
* name="chill_task_task_transition"
|
||||
* "/{_locale}/task/transition/{kind}/{taskId}/{transition}",
|
||||
* name="chill_task_task_transition"
|
||||
* )
|
||||
*
|
||||
* @param string $kind
|
||||
* @param int $taskId
|
||||
* @param string $transition
|
||||
* @param SingleTaskRepository $singleTaskRepository
|
||||
* @param Registry $registry
|
||||
* @param EntityManagerInterface $em
|
||||
* @param Request $request
|
||||
* @param TranslatorInterface $translator
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function applyTransitionAction(
|
||||
@@ -56,91 +59,90 @@ class TaskController extends AbstractController
|
||||
switch ($kind) {
|
||||
case 'single-task':
|
||||
$task = $singleTaskRepository
|
||||
->find($taskId)
|
||||
;
|
||||
->find($taskId);
|
||||
$defaultReturnPath = $this->generateUrl(
|
||||
'chill_task_single_task_show',
|
||||
[
|
||||
[
|
||||
'id' => $task->getId(),
|
||||
'list_params' => $request->query->get('list_params', [])
|
||||
]);
|
||||
'list_params' => $request->query->get('list_params', []),
|
||||
]
|
||||
);
|
||||
$task->getCourse() === null ? $defaultTemplate = '@ChillTask/SingleTask/Person/transition.html.twig' : $defaultTemplate = '@ChillTask/SingleTask/AccompanyingCourse/transition.html.twig';
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return new Response("The type '$kind' is not implemented",
|
||||
Response::HTTP_BAD_REQUEST);
|
||||
return new Response(
|
||||
"The type '{$kind}' is not implemented",
|
||||
Response::HTTP_BAD_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
if (NULL === $task) {
|
||||
throw $this->createNotFoundException("task with id '$taskId' and type "
|
||||
. "'$type' does not exists");
|
||||
if (null === $task) {
|
||||
throw $this->createNotFoundException("task with id '{$taskId}' and type "
|
||||
. "'{$type}' does not exists");
|
||||
}
|
||||
|
||||
|
||||
$workflow = $registry->get($task);
|
||||
|
||||
|
||||
if (!$workflow->can($task, $transition)) {
|
||||
throw $this->createAccessDeniedException('You are not allowed to apply this transition');
|
||||
}
|
||||
$transitionInstance = \array_values( // array_values needed to reset keys (array_filter preserves keys)
|
||||
\array_filter(
|
||||
$workflow->getEnabledTransitions($task),
|
||||
function(Transition $t) use ($transition) {
|
||||
$transitionInstance = array_values( // array_values needed to reset keys (array_filter preserves keys)
|
||||
array_filter(
|
||||
$workflow->getEnabledTransitions($task),
|
||||
function (Transition $t) use ($transition) {
|
||||
return $t->getName() === $transition;
|
||||
}
|
||||
))[0];
|
||||
|
||||
$form = $this->createTransitionForm($task);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
))[0];
|
||||
|
||||
$form = $this->createTransitionForm($task);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
if ($workflow->can($task, $transition)) {
|
||||
$workflow->apply($task, $transition);
|
||||
|
||||
$em->flush();
|
||||
|
||||
$this->addFlash('success', $translator->trans('The transition is successfully applied'));
|
||||
|
||||
} else {
|
||||
$this->addFlash('error', $translator->trans('The transition could not be applied'));
|
||||
}
|
||||
|
||||
|
||||
return $this->redirect($defaultReturnPath);
|
||||
} else {
|
||||
$event = (new UIEvent($kind, $task))
|
||||
->setForm($form)
|
||||
->setTransition($transitionInstance)
|
||||
;
|
||||
|
||||
$eventDispatcher->dispatch(UIEvent::SHOW_TRANSITION_PAGE, $event);
|
||||
|
||||
if ($event->hasResponse()) {
|
||||
return $event->getResponse();
|
||||
} else {
|
||||
// we simply check that the user can see the task. Other ACL checks
|
||||
// should be performed using `guard` events.
|
||||
$this->denyAccessUnlessGranted(TaskVoter::SHOW, $task);
|
||||
|
||||
return $this->render($defaultTemplate, [
|
||||
'task' => $task,
|
||||
'form' => $form->createView(),
|
||||
'transition' => $transitionInstance
|
||||
]);
|
||||
}
|
||||
}
|
||||
$event = (new UIEvent($kind, $task))
|
||||
->setForm($form)
|
||||
->setTransition($transitionInstance);
|
||||
|
||||
$eventDispatcher->dispatch(UIEvent::SHOW_TRANSITION_PAGE, $event);
|
||||
|
||||
if ($event->hasResponse()) {
|
||||
return $event->getResponse();
|
||||
}
|
||||
// we simply check that the user can see the task. Other ACL checks
|
||||
// should be performed using `guard` events.
|
||||
$this->denyAccessUnlessGranted(TaskVoter::SHOW, $task);
|
||||
|
||||
return $this->render($defaultTemplate, [
|
||||
'task' => $task,
|
||||
'form' => $form->createView(),
|
||||
'transition' => $transitionInstance,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param \Chill\TaskBundle\Controller\AbstractTask $task
|
||||
*
|
||||
* @return \Symfony\Component\Form\FormInterface
|
||||
*/
|
||||
protected function createTransitionForm(AbstractTask $task)
|
||||
{
|
||||
$builder = $this->createFormBuilder($task);
|
||||
$builder->add('submit', SubmitType::class);
|
||||
|
||||
|
||||
return $builder->getForm();
|
||||
}
|
||||
}
|
||||
|
@@ -1,37 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* 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/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\DataFixtures\ORM;
|
||||
|
||||
use Chill\MainBundle\DataFixtures\ORM\LoadPermissionsGroup;
|
||||
use Chill\MainBundle\DataFixtures\ORM\LoadScopes;
|
||||
use Chill\MainBundle\Entity\RoleScope;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||
use Doctrine\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>
|
||||
* and a role SEE for administrative.
|
||||
*/
|
||||
class LoadTaskACL extends AbstractFixture implements OrderedFixtureInterface
|
||||
{
|
||||
@@ -40,12 +28,12 @@ class LoadTaskACL extends AbstractFixture implements OrderedFixtureInterface
|
||||
return 16000;
|
||||
}
|
||||
|
||||
|
||||
public function load(ObjectManager $manager)
|
||||
{
|
||||
foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) {
|
||||
$permissionsGroup = $this->getReference($permissionsGroupRef);
|
||||
foreach (LoadScopes::$references as $scopeRef){
|
||||
|
||||
foreach (LoadScopes::$references as $scopeRef) {
|
||||
$scope = $this->getReference($scopeRef);
|
||||
//create permission group
|
||||
switch ($permissionsGroup->getName()) {
|
||||
@@ -53,33 +41,39 @@ class LoadTaskACL extends AbstractFixture implements OrderedFixtureInterface
|
||||
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'))) {
|
||||
if (in_array($scope->getName()['en'], ['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 & Chill_TASK_TASK_DELETE permissions to %s "
|
||||
printf(
|
||||
'Adding CHILL_TASK_TASK_UPDATE & CHILL_TASK_TASK_CREATE & Chill_TASK_TASK_DELETE permissions to %s '
|
||||
. "permission group, scope '%s' \n",
|
||||
$permissionsGroup->getName(), $scope->getName()['en']);
|
||||
$permissionsGroup->getName(),
|
||||
$scope->getName()['en']
|
||||
);
|
||||
$roleScopeUpdate = (new RoleScope())
|
||||
->setRole(TaskVoter::UPDATE)
|
||||
->setScope($scope);
|
||||
->setRole(TaskVoter::UPDATE)
|
||||
->setScope($scope);
|
||||
$permissionsGroup->addRoleScope($roleScopeUpdate);
|
||||
$roleScopeCreateP = (new RoleScope())
|
||||
->setRole(TaskVoter::CREATE_PERSON)
|
||||
->setScope($scope);
|
||||
->setRole(TaskVoter::CREATE_PERSON)
|
||||
->setScope($scope);
|
||||
$permissionsGroup->addRoleScope($roleScopeCreateP);
|
||||
$roleScopeCreateC = (new RoleScope())
|
||||
->setRole(TaskVoter::CREATE_COURSE)
|
||||
->setScope($scope);
|
||||
$permissionsGroup->addRoleScope($roleScopeCreateC);
|
||||
$roleScopeDelete = (new RoleScope())
|
||||
->setRole(TaskVoter::DELETE)
|
||||
->setScope($scope);
|
||||
->setRole(TaskVoter::DELETE)
|
||||
->setScope($scope);
|
||||
$permissionsGroup->addRoleScope($roleScopeDelete);
|
||||
|
||||
$manager->persist($roleScopeUpdate);
|
||||
@@ -87,10 +81,8 @@ class LoadTaskACL extends AbstractFixture implements OrderedFixtureInterface
|
||||
$manager->persist($roleScopeCreateC);
|
||||
$manager->persist($roleScopeDelete);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,31 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
use Symfony\Component\DependencyInjection\Loader;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||
use Chill\TaskBundle\Workflow\TaskWorkflowManager;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||
use Symfony\Component\DependencyInjection\Loader;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
|
||||
/**
|
||||
* This is the class that loads and manages your bundle configuration.
|
||||
*
|
||||
* @link http://symfony.com/doc/current/cookbook/bundles/extension.html
|
||||
* @see http://symfony.com/doc/current/cookbook/bundles/extension.html
|
||||
*/
|
||||
class ChillTaskExtension extends Extension implements PrependExtensionInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
$configuration = new Configuration();
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
|
||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
|
||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../config'));
|
||||
$loader->load('services/controller.yaml');
|
||||
$loader->load('services/security.yaml');
|
||||
$loader->load('services/repositories.yaml');
|
||||
@@ -45,27 +49,27 @@ class ChillTaskExtension extends Extension implements PrependExtensionInterface
|
||||
$this->prependWorkflows($container);
|
||||
}
|
||||
|
||||
protected function prependAuthorization(ContainerBuilder $container)
|
||||
{
|
||||
$container->prependExtensionConfig('security', [
|
||||
'role_hierarchy' => [
|
||||
TaskVoter::UPDATE => [TaskVoter::SHOW],
|
||||
TaskVoter::CREATE_COURSE => [TaskVoter::SHOW],
|
||||
TaskVoter::CREATE_PERSON => [TaskVoter::SHOW],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function prependRoute(ContainerBuilder $container)
|
||||
{
|
||||
//declare routes for task bundle
|
||||
$container->prependExtensionConfig('chill_main', array(
|
||||
'routing' => array(
|
||||
'resources' => array(
|
||||
'@ChillTaskBundle/config/routes.yaml'
|
||||
)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
protected function prependAuthorization(ContainerBuilder $container)
|
||||
{
|
||||
$container->prependExtensionConfig('security', array(
|
||||
'role_hierarchy' => array(
|
||||
TaskVoter::UPDATE => [TaskVoter::SHOW],
|
||||
TaskVoter::CREATE_COURSE => [TaskVoter::SHOW],
|
||||
TaskVoter::CREATE_PERSON => [TaskVoter::SHOW],
|
||||
)
|
||||
));
|
||||
$container->prependExtensionConfig('chill_main', [
|
||||
'routing' => [
|
||||
'resources' => [
|
||||
'@ChillTaskBundle/config/routes.yaml',
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function prependWorkflows(ContainerBuilder $container)
|
||||
@@ -76,29 +80,29 @@ class ChillTaskExtension extends Extension implements PrependExtensionInterface
|
||||
'marking_store' => [
|
||||
'type' => 'multiple_state',
|
||||
'arguments' => [
|
||||
'currentStates'
|
||||
'currentStates',
|
||||
],
|
||||
],
|
||||
'type' => 'state_machine',
|
||||
'type' => 'state_machine',
|
||||
'support_strategy' => TaskWorkflowManager::class,
|
||||
'places' => [ 'new', 'in_progress', 'closed', 'canceled'],
|
||||
'places' => ['new', 'in_progress', 'closed', 'canceled'],
|
||||
'initial_place' => 'new',
|
||||
'transitions' => [
|
||||
'start' => [
|
||||
'from' => 'new',
|
||||
'to' => 'in_progress'
|
||||
'to' => 'in_progress',
|
||||
],
|
||||
'close' => [
|
||||
'from' => ['new', 'in_progress'],
|
||||
'to' => 'closed'
|
||||
'to' => 'closed',
|
||||
],
|
||||
'cancel' => [
|
||||
'from' => ['new', 'in_progress'],
|
||||
'to' => 'canceled'
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
'to' => 'canceled',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@@ -1,47 +1,35 @@
|
||||
<?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\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Chill\TaskBundle\Workflow\TaskWorkflowManager;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Chill\TaskBundle\Templating\UI\CountNotificationTask;
|
||||
use Chill\TaskBundle\Event\Lifecycle\TaskLifecycleEvent;
|
||||
|
||||
/**
|
||||
*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Chill\TaskBundle\Event\Lifecycle\TaskLifecycleEvent;
|
||||
use Chill\TaskBundle\Templating\UI\CountNotificationTask;
|
||||
use Chill\TaskBundle\Workflow\TaskWorkflowManager;
|
||||
use LogicException;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class TaskWorkflowDefinitionCompilerPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition(TaskWorkflowManager::class)) {
|
||||
throw new \LogicException("The service ".TaskWorkflowManager::class." is "
|
||||
. "not registered");
|
||||
throw new LogicException('The service ' . TaskWorkflowManager::class . ' is '
|
||||
. 'not registered');
|
||||
}
|
||||
|
||||
|
||||
$workflowManagerDefinition = $container->getDefinition(TaskWorkflowManager::class);
|
||||
$counterDefinition = $container->getDefinition(CountNotificationTask::class);
|
||||
$lifecycleDefinition = $container->getDefinition(TaskLifecycleEvent::class);
|
||||
|
||||
|
||||
foreach ($container->findTaggedServiceIds('chill_task.workflow_definition') as $id => $tags) {
|
||||
// registering the definition to manager
|
||||
$workflowManagerDefinition
|
||||
@@ -50,21 +38,21 @@ class TaskWorkflowDefinitionCompilerPass implements CompilerPassInterface
|
||||
$definition = $container->getDefinition($id);
|
||||
$workflowManagerDefinition
|
||||
->addTag('kernel.event_listener', [
|
||||
'event' => sprintf('workflow.%s.entered', $definition->getClass()::getAssociatedWorkflowName()),
|
||||
'method' => 'onTaskStateEntered',
|
||||
'priority' => -255
|
||||
]);
|
||||
'event' => sprintf('workflow.%s.entered', $definition->getClass()::getAssociatedWorkflowName()),
|
||||
'method' => 'onTaskStateEntered',
|
||||
'priority' => -255,
|
||||
]);
|
||||
$counterDefinition
|
||||
->addTag('kernel.event_listener', [
|
||||
'event' => sprintf('workflow.%s.entered', $definition->getClass()::getAssociatedWorkflowName()),
|
||||
'method' => 'resetCacheOnNewStates',
|
||||
'priority' => 0
|
||||
'priority' => 0,
|
||||
]);
|
||||
$lifecycleDefinition
|
||||
->addTag('kernel.event_listener', [
|
||||
'event' => sprintf('workflow.%s.transition', $definition->getClass()::getAssociatedWorkflowName()),
|
||||
'method' => 'onTransition',
|
||||
'priority' => 0
|
||||
'priority' => 0,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
@@ -12,9 +19,6 @@ use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
*/
|
||||
class Configuration implements ConfigurationInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('chill_task');
|
||||
|
@@ -1,31 +1,59 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Entity;
|
||||
|
||||
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;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistency;
|
||||
use Chill\MainBundle\Entity\HasScopeInterface;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use function array_fill_keys;
|
||||
use function array_keys;
|
||||
|
||||
/**
|
||||
* AbstractTask
|
||||
* AbstractTask.
|
||||
*
|
||||
* @ORM\MappedSuperclass()
|
||||
* @ORM\MappedSuperclass
|
||||
*/
|
||||
abstract class AbstractTask implements HasScopeInterface, HasCenterInterface
|
||||
{
|
||||
/**
|
||||
* @var User
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="\Chill\MainBundle\Entity\User"
|
||||
* )
|
||||
*/
|
||||
private $assignee;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="type", type="string", length=255)
|
||||
* @var Scope
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="\Chill\MainBundle\Entity\Scope"
|
||||
* )
|
||||
*/
|
||||
private $type;
|
||||
private $circle;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @ORM\Column(name="closed", type="boolean", options={ "default": false })
|
||||
*/
|
||||
private $closed = false;
|
||||
|
||||
/**
|
||||
* @var AccompanyingPeriod
|
||||
* @ORM\ManyToOne(targetEntity="\Chill\PersonBundle\Entity\AccompanyingPeriod")
|
||||
*/
|
||||
private $course;
|
||||
|
||||
/**
|
||||
* @var json
|
||||
@@ -34,78 +62,111 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface
|
||||
*/
|
||||
private $currentStates = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="description", type="text")
|
||||
*/
|
||||
private $description = '';
|
||||
|
||||
/**
|
||||
* @var Person
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="\Chill\PersonBundle\Entity\Person"
|
||||
* )
|
||||
*/
|
||||
private $person;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="title", type="text")
|
||||
* @Assert\NotBlank()
|
||||
* @Assert\NotBlank
|
||||
*/
|
||||
private $title = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="description", type="text")
|
||||
* @ORM\Column(name="type", type="string", length=255)
|
||||
*/
|
||||
private $description = '';
|
||||
private $type;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var User
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="\Chill\MainBundle\Entity\User"
|
||||
* )
|
||||
*/
|
||||
private $assignee;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Person
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="\Chill\PersonBundle\Entity\Person"
|
||||
* )
|
||||
*/
|
||||
private $person;
|
||||
|
||||
|
||||
/**
|
||||
* @var AccompanyingPeriod
|
||||
* @ORM\ManyToOne(targetEntity="\Chill\PersonBundle\Entity\AccompanyingPeriod")
|
||||
*/
|
||||
|
||||
private $course;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Scope
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="\Chill\MainBundle\Entity\Scope"
|
||||
* )
|
||||
*/
|
||||
private $circle;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
* @ORM\Column(name="closed", type="boolean", options={ "default"=false })
|
||||
*/
|
||||
private $closed = false;
|
||||
|
||||
/**
|
||||
* Set type
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return AbstractTask
|
||||
*/
|
||||
public function setType($type)
|
||||
public function getAssignee(): ?User
|
||||
{
|
||||
$this->type = (string) $type;
|
||||
return $this->assignee;
|
||||
}
|
||||
|
||||
return $this;
|
||||
public function getCenter(): ?\Chill\MainBundle\Entity\Center
|
||||
{
|
||||
if ($this->getPerson() instanceof Person) {
|
||||
return $this->getPerson()->getCenter();
|
||||
}
|
||||
|
||||
return $this->getCourse()->getCenter();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getCircle(): ?Scope
|
||||
{
|
||||
return $this->circle;
|
||||
}
|
||||
|
||||
public function getContext()
|
||||
{
|
||||
return $this->getPerson() ?? $this->getCourse();
|
||||
}
|
||||
|
||||
public function getCourse(): ?AccompanyingPeriod
|
||||
{
|
||||
return $this->course;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type
|
||||
* Get currentStates.
|
||||
*
|
||||
* The states are returned as required by marking store format.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCurrentStates()
|
||||
{
|
||||
return array_fill_keys($this->currentStates, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get description.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function getPerson(): ?Person
|
||||
{
|
||||
return $this->person;
|
||||
}
|
||||
|
||||
public function getScope(): ?Scope
|
||||
{
|
||||
return $this->getCircle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -114,8 +175,39 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function isClosed(): bool
|
||||
{
|
||||
return $this->closed;
|
||||
}
|
||||
|
||||
public function setAssignee(?User $assignee = null)
|
||||
{
|
||||
$this->assignee = $assignee;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCircle(Scope $circle)
|
||||
{
|
||||
$this->circle = $circle;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setClosed(bool $closed)
|
||||
{
|
||||
$this->closed = $closed;
|
||||
}
|
||||
|
||||
public function setCourse(AccompanyingPeriod $course)
|
||||
{
|
||||
$this->course = $course;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set currentStates
|
||||
* Set currentStates.
|
||||
*
|
||||
* The current states are sorted in a single array, non associative.
|
||||
*
|
||||
@@ -125,25 +217,34 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface
|
||||
*/
|
||||
public function setCurrentStates($currentStates)
|
||||
{
|
||||
$this->currentStates = \array_keys($currentStates);
|
||||
$this->currentStates = array_keys($currentStates);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get currentStates
|
||||
* Set description.
|
||||
*
|
||||
* The states are returned as required by marking store format.
|
||||
* @param string $description
|
||||
*
|
||||
* @return array
|
||||
* @return AbstractTask
|
||||
*/
|
||||
public function getCurrentStates()
|
||||
public function setDescription($description)
|
||||
{
|
||||
return \array_fill_keys($this->currentStates, 1);
|
||||
$this->description = (string) $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPerson(Person $person)
|
||||
{
|
||||
$this->person = $person;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set title
|
||||
* Set title.
|
||||
*
|
||||
* @param string $title
|
||||
*
|
||||
@@ -157,120 +258,16 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get title
|
||||
* Set type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set description
|
||||
*
|
||||
* @param string $description
|
||||
* @param string $type
|
||||
*
|
||||
* @return AbstractTask
|
||||
*/
|
||||
public function setDescription($description)
|
||||
public function setType($type)
|
||||
{
|
||||
$this->description = (string) $description;
|
||||
$this->type = (string) $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function getAssignee(): ?User
|
||||
{
|
||||
return $this->assignee;
|
||||
}
|
||||
|
||||
public function getPerson(): ?Person
|
||||
{
|
||||
return $this->person;
|
||||
}
|
||||
|
||||
public function getCourse(): ?AccompanyingPeriod
|
||||
{
|
||||
return $this->course;
|
||||
}
|
||||
|
||||
public function getCircle(): ?Scope
|
||||
{
|
||||
return $this->circle;
|
||||
}
|
||||
|
||||
public function setAssignee(User $assignee = null)
|
||||
{
|
||||
$this->assignee = $assignee;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPerson(Person $person)
|
||||
{
|
||||
$this->person = $person;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCourse(AccompanyingPeriod $course)
|
||||
{
|
||||
$this->course = $course;
|
||||
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();
|
||||
} else {
|
||||
return $this->getCourse()->getCenter();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public function getContext()
|
||||
{
|
||||
return $this->getPerson() ?? $this->getCourse();
|
||||
}
|
||||
|
||||
public function getScope(): ?\Chill\MainBundle\Entity\Scope
|
||||
{
|
||||
return $this->getCircle();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isClosed(): bool
|
||||
{
|
||||
return $this->closed;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bool $closed
|
||||
*/
|
||||
public function setClosed(bool $closed)
|
||||
{
|
||||
$this->closed = $closed;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,20 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use DateTime;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* RecurringTask
|
||||
* RecurringTask.
|
||||
*
|
||||
* @ORM\Table(name="chill_task.recurring_task")
|
||||
* @ORM\Entity(repositoryClass="Chill\TaskBundle\Repository\RecurringTaskRepository")
|
||||
*/
|
||||
class RecurringTask extends AbstractTask
|
||||
{
|
||||
/**
|
||||
* @var DateTime
|
||||
*
|
||||
* @ORM\Column(name="first_occurence_end_date", type="date")
|
||||
*/
|
||||
private $firstOccurenceEndDate;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
@@ -25,14 +40,7 @@ class RecurringTask extends AbstractTask
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*
|
||||
* @ORM\Column(name="first_occurence_end_date", type="date")
|
||||
*/
|
||||
private $firstOccurenceEndDate;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
* @var DateTime
|
||||
*
|
||||
* @ORM\Column(name="last_occurence_end_date", type="date")
|
||||
*/
|
||||
@@ -58,27 +66,34 @@ class RecurringTask extends AbstractTask
|
||||
* @ORM\Column(name="occurence_warning_interval", type="dateinterval", nullable=true)
|
||||
*/
|
||||
private $occurenceWarningInterval;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Collection
|
||||
*
|
||||
*
|
||||
* @ORM\OneToMany(
|
||||
* targetEntity="SingleTask",
|
||||
* mappedBy="recurringTask"
|
||||
* targetEntity="SingleTask",
|
||||
* mappedBy="recurringTask"
|
||||
* )
|
||||
*/
|
||||
private $singleTasks;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->singleTasks = new ArrayCollection();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get id
|
||||
* Get firstOccurenceEndDate.
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getFirstOccurenceEndDate()
|
||||
{
|
||||
return $this->firstOccurenceEndDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -88,9 +103,49 @@ class RecurringTask extends AbstractTask
|
||||
}
|
||||
|
||||
/**
|
||||
* Set firstOccurenceEndDate
|
||||
* Get lastOccurenceEndDate.
|
||||
*
|
||||
* @param \DateTime $firstOccurenceEndDate
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getLastOccurenceEndDate()
|
||||
{
|
||||
return $this->lastOccurenceEndDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get occurenceFrequency.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOccurenceFrequency()
|
||||
{
|
||||
return $this->occurenceFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get occurenceStartDate.
|
||||
*
|
||||
* @return dateinterval
|
||||
*/
|
||||
public function getOccurenceStartDate()
|
||||
{
|
||||
return $this->occurenceStartDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get occurenceWarningInterval.
|
||||
*
|
||||
* @return dateinterval
|
||||
*/
|
||||
public function getOccurenceWarningInterval()
|
||||
{
|
||||
return $this->occurenceWarningInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set firstOccurenceEndDate.
|
||||
*
|
||||
* @param DateTime $firstOccurenceEndDate
|
||||
*
|
||||
* @return RecurringTask
|
||||
*/
|
||||
@@ -102,19 +157,9 @@ class RecurringTask extends AbstractTask
|
||||
}
|
||||
|
||||
/**
|
||||
* Get firstOccurenceEndDate
|
||||
* Set lastOccurenceEndDate.
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getFirstOccurenceEndDate()
|
||||
{
|
||||
return $this->firstOccurenceEndDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set lastOccurenceEndDate
|
||||
*
|
||||
* @param \DateTime $lastOccurenceEndDate
|
||||
* @param DateTime $lastOccurenceEndDate
|
||||
*
|
||||
* @return RecurringTask
|
||||
*/
|
||||
@@ -126,17 +171,7 @@ class RecurringTask extends AbstractTask
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lastOccurenceEndDate
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getLastOccurenceEndDate()
|
||||
{
|
||||
return $this->lastOccurenceEndDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set occurenceFrequency
|
||||
* Set occurenceFrequency.
|
||||
*
|
||||
* @param string $occurenceFrequency
|
||||
*
|
||||
@@ -150,17 +185,7 @@ class RecurringTask extends AbstractTask
|
||||
}
|
||||
|
||||
/**
|
||||
* Get occurenceFrequency
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOccurenceFrequency()
|
||||
{
|
||||
return $this->occurenceFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set occurenceStartDate
|
||||
* Set occurenceStartDate.
|
||||
*
|
||||
* @param dateinterval $occurenceStartDate
|
||||
*
|
||||
@@ -174,17 +199,7 @@ class RecurringTask extends AbstractTask
|
||||
}
|
||||
|
||||
/**
|
||||
* Get occurenceStartDate
|
||||
*
|
||||
* @return dateinterval
|
||||
*/
|
||||
public function getOccurenceStartDate()
|
||||
{
|
||||
return $this->occurenceStartDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set occurenceWarningInterval
|
||||
* Set occurenceWarningInterval.
|
||||
*
|
||||
* @param dateinterval $occurenceWarningInterval
|
||||
*
|
||||
@@ -196,15 +211,4 @@ class RecurringTask extends AbstractTask
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get occurenceWarningInterval
|
||||
*
|
||||
* @return dateinterval
|
||||
*/
|
||||
public function getOccurenceWarningInterval()
|
||||
{
|
||||
return $this->occurenceWarningInterval;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,36 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Entity;
|
||||
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
|
||||
/**
|
||||
* SingleTask
|
||||
* SingleTask.
|
||||
*
|
||||
* @ORM\Table(
|
||||
* name="chill_task.single_task",
|
||||
* indexes={
|
||||
* @ORM\Index(
|
||||
* name="by_type",
|
||||
* columns={ "type" }
|
||||
* ),
|
||||
* @ORM\Index(
|
||||
* name="by_current_state",
|
||||
* columns={ "current_states" }
|
||||
* ),
|
||||
* @ORM\Index(
|
||||
* name="by_end_date",
|
||||
* columns={ "end_date" }
|
||||
* )
|
||||
* }
|
||||
* name="chill_task.single_task",
|
||||
* indexes={
|
||||
* @ORM\Index(
|
||||
* name="by_type",
|
||||
* columns={ "type" }
|
||||
* ),
|
||||
* @ORM\Index(
|
||||
* name="by_current_state",
|
||||
* columns={ "current_states" }
|
||||
* ),
|
||||
* @ORM\Index(
|
||||
* name="by_end_date",
|
||||
* columns={ "end_date" }
|
||||
* )
|
||||
* }
|
||||
* )
|
||||
* @ORM\Entity(repositoryClass="Chill\TaskBundle\Repository\SingleTaskRepository")
|
||||
*/
|
||||
class SingleTask extends AbstractTask
|
||||
{
|
||||
/**
|
||||
* @var DateTime
|
||||
*
|
||||
* @ORM\Column(name="end_date", type="date", nullable=true)
|
||||
* @Assert\Date
|
||||
*/
|
||||
private $endDate;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
@@ -41,75 +59,72 @@ class SingleTask extends AbstractTask
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*
|
||||
* @ORM\Column(name="start_date", type="date", nullable=true)
|
||||
* @Assert\Date()
|
||||
*
|
||||
* @Assert\Expression(
|
||||
* "value === null or this.getEndDate() === null or value < this.getEndDate()",
|
||||
* message="The start date must be before the end date"
|
||||
* )
|
||||
*
|
||||
* @Assert\Expression(
|
||||
* "value === null or this.getWarningDate() === null or this.getWarningDate() > this.getStartDate()",
|
||||
* message="The start date must be before warning date"
|
||||
* )
|
||||
*/
|
||||
private $startDate;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*
|
||||
* @ORM\Column(name="end_date", type="date", nullable=true)
|
||||
* @Assert\Date()
|
||||
*/
|
||||
private $endDate;
|
||||
|
||||
/**
|
||||
* @var \DateInterval
|
||||
* and this.getEndDate() === null
|
||||
*
|
||||
* @ORM\Column(name="warning_interval", type="dateinterval", nullable=true)
|
||||
*
|
||||
* @Assert\Expression(
|
||||
* "!(value != null and this.getEndDate() == null)",
|
||||
* message="An end date is required if a warning interval is set"
|
||||
* )
|
||||
*
|
||||
*
|
||||
*/
|
||||
private $warningInterval;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var RecurringTask
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="RecurringTask",
|
||||
* inversedBy="singleTasks"
|
||||
* targetEntity="RecurringTask",
|
||||
* inversedBy="singleTasks"
|
||||
* )
|
||||
*/
|
||||
private $recurringTask;
|
||||
|
||||
/**
|
||||
* @var DateTime
|
||||
*
|
||||
* @ORM\Column(name="start_date", type="date", nullable=true)
|
||||
* @Assert\Date
|
||||
*
|
||||
* @Assert\Expression(
|
||||
* "value === null or this.getEndDate() === null or value < this.getEndDate()",
|
||||
* message="The start date must be before the end date"
|
||||
* )
|
||||
*
|
||||
* @Assert\Expression(
|
||||
* "value === null or this.getWarningDate() === null or this.getWarningDate() > this.getStartDate()",
|
||||
* message="The start date must be before warning date"
|
||||
* )
|
||||
*/
|
||||
private $startDate;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\Common\Collections\Collection
|
||||
* @ORM\OneToMany(
|
||||
* targetEntity="\Chill\TaskBundle\Entity\Task\SingleTaskPlaceEvent",
|
||||
* mappedBy="task",
|
||||
* cascade={ "remove" }
|
||||
* targetEntity="\Chill\TaskBundle\Entity\Task\SingleTaskPlaceEvent",
|
||||
* mappedBy="task",
|
||||
* cascade={ "remove" }
|
||||
* )
|
||||
*/
|
||||
private $taskPlaceEvents;
|
||||
|
||||
/**
|
||||
* @var DateInterval
|
||||
* and this.getEndDate() === null
|
||||
*
|
||||
* @ORM\Column(name="warning_interval", type="dateinterval", nullable=true)
|
||||
*
|
||||
* @Assert\Expression(
|
||||
* "!(value != null and this.getEndDate() == null)",
|
||||
* message="An end date is required if a warning interval is set"
|
||||
* )
|
||||
*/
|
||||
private $warningInterval;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->taskPlaceEvents = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get endDate.
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getEndDate()
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id
|
||||
* Get id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -118,85 +133,33 @@ class SingleTask extends AbstractTask
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set startDate
|
||||
*
|
||||
* @param \DateTime $startDate
|
||||
*
|
||||
* @return SingleTask
|
||||
*/
|
||||
public function setStartDate($startDate)
|
||||
public function getRecurringTask(): RecurringTask
|
||||
{
|
||||
$this->startDate = $startDate;
|
||||
|
||||
return $this;
|
||||
return $this->recurringTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get startDate
|
||||
* Get startDate.
|
||||
*
|
||||
* @return \DateTime
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getStartDate()
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set endDate
|
||||
*
|
||||
* @param \DateTime $endDate
|
||||
*
|
||||
* @return SingleTask
|
||||
*/
|
||||
public function setEndDate($endDate)
|
||||
public function getTaskPlaceEvents(): Collection
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get endDate
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getEndDate()
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set warningInterval
|
||||
*
|
||||
* @param string $warningInterval
|
||||
*
|
||||
* @return SingleTask
|
||||
*/
|
||||
public function setWarningInterval($warningInterval)
|
||||
{
|
||||
$this->warningInterval = $warningInterval;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get warningInterval
|
||||
*
|
||||
* @return \DateInterval
|
||||
*/
|
||||
public function getWarningInterval()
|
||||
{
|
||||
return $this->warningInterval;
|
||||
return $this->taskPlaceEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Warning date, computed from the difference between the
|
||||
* end date and the warning interval
|
||||
* end date and the warning interval.
|
||||
*
|
||||
* Return null if warningDate or endDate is null
|
||||
*
|
||||
* @return \DateTimeImmutable
|
||||
* @return DateTimeImmutable
|
||||
*/
|
||||
public function getWarningDate()
|
||||
{
|
||||
@@ -208,23 +171,51 @@ class SingleTask extends AbstractTask
|
||||
return null;
|
||||
}
|
||||
|
||||
return \DateTimeImmutable::createFromMutable($this->getEndDate())
|
||||
return DateTimeImmutable::createFromMutable($this->getEndDate())
|
||||
->sub($this->getWarningInterval());
|
||||
}
|
||||
|
||||
function getRecurringTask(): RecurringTask
|
||||
/**
|
||||
* Get warningInterval.
|
||||
*
|
||||
* @return DateInterval
|
||||
*/
|
||||
public function getWarningInterval()
|
||||
{
|
||||
return $this->recurringTask;
|
||||
return $this->warningInterval;
|
||||
}
|
||||
|
||||
function setRecurringTask(RecurringTask $recurringTask)
|
||||
/**
|
||||
* Set endDate.
|
||||
*
|
||||
* @param DateTime $endDate
|
||||
*
|
||||
* @return SingleTask
|
||||
*/
|
||||
public function setEndDate($endDate)
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRecurringTask(RecurringTask $recurringTask)
|
||||
{
|
||||
$this->recurringTask = $recurringTask;
|
||||
}
|
||||
|
||||
public function getTaskPlaceEvents(): Collection
|
||||
/**
|
||||
* Set startDate.
|
||||
*
|
||||
* @param DateTime $startDate
|
||||
*
|
||||
* @return SingleTask
|
||||
*/
|
||||
public function setStartDate($startDate)
|
||||
{
|
||||
return $this->taskPlaceEvents;
|
||||
$this->startDate = $startDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setTaskPlaceEvents(Collection $taskPlaceEvents)
|
||||
@@ -233,5 +224,18 @@ class SingleTask extends AbstractTask
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set warningInterval.
|
||||
*
|
||||
* @param string $warningInterval
|
||||
*
|
||||
* @return SingleTask
|
||||
*/
|
||||
public function setWarningInterval($warningInterval)
|
||||
{
|
||||
$this->warningInterval = $warningInterval;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@@ -1,17 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Entity\Task;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* AbstractTaskPlaceEvent
|
||||
* AbstractTaskPlaceEvent.
|
||||
*
|
||||
* @ORM\MappedSuperclass()
|
||||
* @ORM\MappedSuperclass
|
||||
*/
|
||||
class AbstractTaskPlaceEvent
|
||||
{
|
||||
/**
|
||||
* @var User
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="\Chill\MainBundle\Entity\User"
|
||||
* )
|
||||
*/
|
||||
private $author;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="data", type="json")
|
||||
*/
|
||||
private $data = [];
|
||||
|
||||
/**
|
||||
* @var datetime_immutable
|
||||
*
|
||||
* @ORM\Column(name="datetime", type="datetime_immutable")
|
||||
*/
|
||||
private $datetime;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
@@ -21,13 +51,6 @@ class AbstractTaskPlaceEvent
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var datetime_immutable
|
||||
*
|
||||
* @ORM\Column(name="datetime", type="datetime_immutable")
|
||||
*/
|
||||
private $datetime;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
@@ -35,49 +58,24 @@ class AbstractTaskPlaceEvent
|
||||
*/
|
||||
private $transition = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="data", type="json")
|
||||
*/
|
||||
private $data = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @var User
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="\Chill\MainBundle\Entity\User"
|
||||
* )
|
||||
*/
|
||||
private $author;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->datetime = new \DateTimeImmutable('now');
|
||||
$this->datetime = new DateTimeImmutable('now');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
public function getAuthor(): User
|
||||
{
|
||||
return $this->id;
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set datetime.
|
||||
* Get data.
|
||||
*
|
||||
* @param datetime_immutable $datetime
|
||||
*
|
||||
* @return AbstractTaskPlaceEvent
|
||||
* @return string
|
||||
*/
|
||||
public function setDatetime($datetime)
|
||||
public function getData()
|
||||
{
|
||||
$this->datetime = $datetime;
|
||||
|
||||
return $this;
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,17 +89,13 @@ class AbstractTaskPlaceEvent
|
||||
}
|
||||
|
||||
/**
|
||||
* Set transition.
|
||||
* Get id.
|
||||
*
|
||||
* @param string $transition
|
||||
*
|
||||
* @return AbstractTaskPlaceEvent
|
||||
* @return int
|
||||
*/
|
||||
public function setTransition($transition)
|
||||
public function getId()
|
||||
{
|
||||
$this->transition = $transition;
|
||||
|
||||
return $this;
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,6 +108,13 @@ class AbstractTaskPlaceEvent
|
||||
return $this->transition;
|
||||
}
|
||||
|
||||
public function setAuthor(User $author)
|
||||
{
|
||||
$this->author = $author;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data.
|
||||
*
|
||||
@@ -129,26 +130,30 @@ class AbstractTaskPlaceEvent
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data.
|
||||
* Set datetime.
|
||||
*
|
||||
* @return string
|
||||
* @param datetime_immutable $datetime
|
||||
*
|
||||
* @return AbstractTaskPlaceEvent
|
||||
*/
|
||||
public function getData()
|
||||
public function setDatetime($datetime)
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getAuthor(): User
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
$this->datetime = $datetime;
|
||||
|
||||
public function setAuthor(User $author)
|
||||
{
|
||||
$this->author = $author;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set transition.
|
||||
*
|
||||
* @param string $transition
|
||||
*
|
||||
* @return AbstractTaskPlaceEvent
|
||||
*/
|
||||
public function setTransition($transition)
|
||||
{
|
||||
$this->transition = $transition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@@ -1,56 +1,43 @@
|
||||
<?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\Entity\Task;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Chill\TaskBundle\Entity\SingleTask;
|
||||
|
||||
/**
|
||||
*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Entity\Task;
|
||||
|
||||
use Chill\TaskBundle\Entity\SingleTask;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Table(
|
||||
* name="chill_task.single_task_place_event",
|
||||
* indexes={
|
||||
* @ORM\Index(
|
||||
* name="transition_task_date",
|
||||
* columns={"task_id", "transition", "datetime"}
|
||||
* ),
|
||||
* @ORM\Index(
|
||||
* name="transition_task",
|
||||
* columns={"task_id", "transition"}
|
||||
* )
|
||||
* })
|
||||
* @ORM\Entity()
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* name="chill_task.single_task_place_event",
|
||||
* indexes={
|
||||
* @ORM\Index(
|
||||
* name="transition_task_date",
|
||||
* columns={"task_id", "transition", "datetime"}
|
||||
* ),
|
||||
* @ORM\Index(
|
||||
* name="transition_task",
|
||||
* columns={"task_id", "transition"}
|
||||
* )
|
||||
* })
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class SingleTaskPlaceEvent extends AbstractTaskPlaceEvent
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var SingleTask
|
||||
* @ORM\ManyToOne(
|
||||
* targetEntity="\Chill\TaskBundle\Entity\SingleTask",
|
||||
* inversedBy="taskPlaceEvents"
|
||||
* targetEntity="\Chill\TaskBundle\Entity\SingleTask",
|
||||
* inversedBy="taskPlaceEvents"
|
||||
* )
|
||||
*/
|
||||
protected $task;
|
||||
|
||||
|
||||
public function getTask(): SingleTask
|
||||
{
|
||||
return $this->task;
|
||||
@@ -59,9 +46,7 @@ class SingleTaskPlaceEvent extends AbstractTaskPlaceEvent
|
||||
public function setTask(SingleTask $task)
|
||||
{
|
||||
$this->task = $task;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,90 +1,73 @@
|
||||
<?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\Event\Lifecycle;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Chill\TaskBundle\Event\TaskEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Chill\TaskBundle\Entity\Task\SingleTaskPlaceEvent;
|
||||
use Symfony\Component\Workflow\Event\Event as WorkflowEvent;
|
||||
|
||||
/**
|
||||
*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Event\Lifecycle;
|
||||
|
||||
use Chill\TaskBundle\Entity\Task\SingleTaskPlaceEvent;
|
||||
use Chill\TaskBundle\Event\TaskEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Workflow\Event\Event as WorkflowEvent;
|
||||
|
||||
class TaskLifecycleEvent implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var TokenStorageInterface
|
||||
*/
|
||||
protected $tokenStorage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
|
||||
/**
|
||||
* @var TokenStorageInterface
|
||||
*/
|
||||
protected $tokenStorage;
|
||||
|
||||
public function __construct(
|
||||
TokenStorageInterface $tokenStorage,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
EntityManagerInterface $em
|
||||
) {
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
TaskEvent::PERSIST => [
|
||||
'onTaskPersist'
|
||||
]
|
||||
'onTaskPersist',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function onTaskPersist(TaskEvent $e)
|
||||
{
|
||||
$task = $e->getTask();
|
||||
$user = $this->tokenStorage->getToken()->getUser();
|
||||
|
||||
|
||||
$event = (new SingleTaskPlaceEvent())
|
||||
->setTask($task)
|
||||
->setAuthor($user)
|
||||
->setTransition('_creation')
|
||||
->setData([
|
||||
'new_states' => $task->getCurrentStates()
|
||||
])
|
||||
;
|
||||
|
||||
'new_states' => $task->getCurrentStates(),
|
||||
]);
|
||||
|
||||
$task->getTaskPlaceEvents()->add($event);
|
||||
|
||||
|
||||
$this->em->persist($event);
|
||||
}
|
||||
|
||||
|
||||
public function onTransition(WorkflowEvent $e)
|
||||
{
|
||||
$task = $e->getSubject();
|
||||
$user = $this->tokenStorage->getToken()->getUser();
|
||||
|
||||
|
||||
$event = (new SingleTaskPlaceEvent())
|
||||
->setTask($task)
|
||||
->setAuthor($user)
|
||||
@@ -92,13 +75,11 @@ class TaskLifecycleEvent implements EventSubscriberInterface
|
||||
->setData([
|
||||
'old_states' => $e->getTransition()->getFroms(),
|
||||
'new_states' => $e->getTransition()->getTos(),
|
||||
'workflow' => $e->getWorkflowName()
|
||||
])
|
||||
;
|
||||
|
||||
'workflow' => $e->getWorkflowName(),
|
||||
]);
|
||||
|
||||
$task->getTaskPlaceEvents()->add($event);
|
||||
|
||||
|
||||
$this->em->persist($event);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,45 +1,31 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* 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/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Event;
|
||||
|
||||
use Chill\TaskBundle\Entity\AbstractTask;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class TaskEvent extends Event
|
||||
{
|
||||
const PERSIST = 'chill_task.task_persist';
|
||||
|
||||
public const PERSIST = 'chill_task.task_persist';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var AbstractTask
|
||||
*/
|
||||
protected $task;
|
||||
|
||||
|
||||
public function __construct(AbstractTask $task)
|
||||
{
|
||||
$this->task = $task;
|
||||
}
|
||||
|
||||
|
||||
public function getTask(): AbstractTask
|
||||
{
|
||||
return $this->task;
|
||||
@@ -48,8 +34,7 @@ class TaskEvent extends Event
|
||||
public function setTask(AbstractTask $task)
|
||||
{
|
||||
$this->task = $task;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,81 +1,60 @@
|
||||
<?php
|
||||
/*
|
||||
*
|
||||
*/
|
||||
namespace Chill\TaskBundle\Event\UI;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Chill\TaskBundle\Entity\AbstractTask;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Workflow\Transition;
|
||||
|
||||
/**
|
||||
*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Event\UI;
|
||||
|
||||
use Chill\TaskBundle\Entity\AbstractTask;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Workflow\Transition;
|
||||
|
||||
class UIEvent extends Event
|
||||
{
|
||||
const SHOW_TRANSITION_PAGE = 'chill_task.show_transition_page';
|
||||
const EDIT_FORM = 'chill_task.edit_form';
|
||||
const EDIT_PAGE = 'chill_task.edit_page';
|
||||
|
||||
public const EDIT_FORM = 'chill_task.edit_form';
|
||||
|
||||
public const EDIT_PAGE = 'chill_task.edit_page';
|
||||
|
||||
public const SHOW_TRANSITION_PAGE = 'chill_task.show_transition_page';
|
||||
|
||||
/**
|
||||
* @var FormInterface|null
|
||||
*/
|
||||
protected $form;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $kind;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @var AbstractTask
|
||||
*/
|
||||
protected $task;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Response|null
|
||||
*/
|
||||
protected $response = null;
|
||||
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var FormInterface|null
|
||||
* @var AbstractTask
|
||||
*/
|
||||
protected $form = null;
|
||||
|
||||
protected $task;
|
||||
|
||||
/**
|
||||
* @var Transition
|
||||
*/
|
||||
protected $transition = null;
|
||||
|
||||
protected $transition;
|
||||
|
||||
public function __construct($kind, AbstractTask $task)
|
||||
{
|
||||
$this->kind = $kind;
|
||||
$this->task = $task;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getKind()
|
||||
{
|
||||
return $this->kind;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return AbstractTask
|
||||
*/
|
||||
public function getTask(): AbstractTask
|
||||
{
|
||||
return $this->task;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return FormInterface|null
|
||||
*/
|
||||
public function getForm()
|
||||
@@ -83,15 +62,25 @@ class UIEvent extends Event
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
public function setForm(FormInterface $form)
|
||||
{
|
||||
$this->form = $form;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getKind()
|
||||
{
|
||||
return $this->kind;
|
||||
}
|
||||
|
||||
public function getResponse(): Response
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
public function getTask(): AbstractTask
|
||||
{
|
||||
return $this->task;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Transition
|
||||
*/
|
||||
public function getTransition()
|
||||
@@ -99,38 +88,32 @@ class UIEvent extends Event
|
||||
return $this->transition;
|
||||
}
|
||||
|
||||
public function setTransition(Transition $transition)
|
||||
{
|
||||
$this->transition = $transition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getResponse(): Response
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Response $response
|
||||
* @return $this
|
||||
*/
|
||||
public function setResponse(Response $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasResponse()
|
||||
{
|
||||
return $this->response instanceof Response;
|
||||
}
|
||||
|
||||
public function setForm(FormInterface $form)
|
||||
{
|
||||
$this->form = $form;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setResponse(Response $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setTransition(Transition $transition)
|
||||
{
|
||||
$this->transition = $transition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@@ -1,75 +1,60 @@
|
||||
<?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 Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Chill\TaskBundle\Repository\SingleTaskRepository;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Chill\TaskBundle\Entity\SingleTask;
|
||||
use Chill\PersonBundle\Form\Type\PickPersonType;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Chill\PersonBundle\Form\DataTransformer\PersonToIdTransformer;
|
||||
use Chill\TaskBundle\Workflow\TaskWorkflowManager;
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Form\DataTransformer\PersonToIdTransformer;
|
||||
use Chill\PersonBundle\Form\Type\PickPersonType;
|
||||
use Chill\TaskBundle\Entity\SingleTask;
|
||||
use Chill\TaskBundle\Repository\SingleTaskRepository;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use Chill\TaskBundle\Workflow\TaskWorkflowManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
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\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use function array_combine;
|
||||
use function array_map;
|
||||
|
||||
class SingleTaskListType extends AbstractType
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TokenStorageInterface
|
||||
*/
|
||||
protected $tokenStorage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
|
||||
/**
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TaskWorkflowManager
|
||||
*/
|
||||
protected $taskWorkflowManager;
|
||||
|
||||
|
||||
/**
|
||||
* @var TokenStorageInterface
|
||||
*/
|
||||
protected $tokenStorage;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $em,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
EntityManagerInterface $em,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
AuthorizationHelper $authorizationHelper,
|
||||
TaskWorkflowManager $taskWorkflowManager
|
||||
) {
|
||||
@@ -79,7 +64,6 @@ class SingleTaskListType extends AbstractType
|
||||
$this->taskWorkflowManager = $taskWorkflowManager;
|
||||
}
|
||||
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$statuses = [
|
||||
@@ -87,56 +71,54 @@ class SingleTaskListType extends AbstractType
|
||||
'Tasks with expired deadline' => SingleTaskRepository::DATE_STATUS_ENDED,
|
||||
'Tasks with warning deadline reached' => SingleTaskRepository::DATE_STATUS_WARNING,
|
||||
'Current tasks' => SingleTaskRepository::DATE_STATUS_CURRENT,
|
||||
'Closed tasks' => 'closed'
|
||||
'Closed tasks' => 'closed',
|
||||
];
|
||||
|
||||
|
||||
$builder
|
||||
->add('user_id', ChoiceType::class, [
|
||||
'choices' => $this->getUserChoices($options),
|
||||
'placeholder' => 'Any user',
|
||||
'required' => false,
|
||||
'label' => 'Assignee'
|
||||
])
|
||||
;
|
||||
|
||||
'label' => 'Assignee',
|
||||
]);
|
||||
|
||||
if ($options['add_status']) {
|
||||
$builder
|
||||
->add('status', ChoiceType::class, [
|
||||
'choices' => $statuses,
|
||||
'expanded' => true,
|
||||
'multiple' => true,
|
||||
'label' => 'status'
|
||||
'label' => 'status',
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
if ($options['add_type']) {
|
||||
$types = $this->getTaskTypesChoices($options);
|
||||
|
||||
|
||||
if (count($types) > 0) {
|
||||
$builder->add('types', ChoiceType::class, [
|
||||
'choices' => $types,
|
||||
'required' => false,
|
||||
'expanded' => true,
|
||||
'multiple' => true,
|
||||
'label' => 'Task types'
|
||||
'label' => 'Task types',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($options['person'] === null) {
|
||||
|
||||
if (null === $options['person']) {
|
||||
$builder
|
||||
->add('person_id', PickPersonType::class, [
|
||||
'centers' => $this->authorizationHelper
|
||||
->getReachableCenters(
|
||||
$this->tokenStorage->getToken()->getUser(),
|
||||
new Role(TaskVoter::SHOW)
|
||||
),
|
||||
),
|
||||
'required' => false,
|
||||
'label' => 'Associated person'
|
||||
])
|
||||
;
|
||||
'label' => 'Associated person',
|
||||
]);
|
||||
$reachablesCenters = $this->getReachablesCenters();
|
||||
|
||||
if (count($reachablesCenters) > 1) {
|
||||
$builder
|
||||
->add('center_id', EntityType::class, [
|
||||
@@ -144,7 +126,7 @@ class SingleTaskListType extends AbstractType
|
||||
'choices' => $reachablesCenters,
|
||||
'label' => 'Center',
|
||||
'required' => false,
|
||||
'placeholder' => 'All centers'
|
||||
'placeholder' => 'All centers',
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
@@ -152,130 +134,8 @@ class SingleTaskListType extends AbstractType
|
||||
$builder
|
||||
->add('person_id', HiddenType::class);
|
||||
$builder->get('person_id')
|
||||
->addModelTransformer(new PersonToIdTransformer($this->em))
|
||||
;
|
||||
->addModelTransformer(new PersonToIdTransformer($this->em));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected function getUserChoices($options)
|
||||
{
|
||||
$users = $this->getUsersAssigneedToTask($options);
|
||||
$choices = \array_combine(
|
||||
// get usernames
|
||||
\array_map(function(User $user) { return $user->getUsername(); }, $users),
|
||||
// get ids
|
||||
\array_map(function(User $user) { return $user->getId(); }, $users)
|
||||
);
|
||||
$choices['Unassigned'] = '_unassigned';
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
protected function getTaskTypesChoices($options)
|
||||
{
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
$user = $this->tokenStorage->getToken()->getUser();
|
||||
$role = new Role(TaskVoter::SHOW);
|
||||
$centers = $this->authorizationHelper->getReachableCenters($user, $role);
|
||||
|
||||
$qb->select('DISTINCT task.type AS type')
|
||||
->from(SingleTask::class, 'task')
|
||||
->join('task.person', 'person')
|
||||
;
|
||||
|
||||
$i = 0;
|
||||
$orCenters = $qb->expr()->orX();
|
||||
foreach($centers as $center) {
|
||||
$circles = $this->authorizationHelper->getReachableCircles($user, $role, $center);
|
||||
|
||||
if (count($circles) > 0) {
|
||||
$andX = $qb->expr()->andX();
|
||||
$andX
|
||||
->add($qb->expr()->eq('person.center', ':center_'.$i))
|
||||
->add($qb->expr()->in('task.circle', ':circles_'.$i))
|
||||
;
|
||||
$orCenters->add($andX);
|
||||
|
||||
$qb
|
||||
->setParameter('center_'.$i, $center)
|
||||
->setParameter('circles_'.$i, $circles)
|
||||
;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($i > 0) {
|
||||
$qb->where($orCenters);
|
||||
}
|
||||
|
||||
$types = $qb->getQuery()->getResult();
|
||||
|
||||
$choices = [];
|
||||
|
||||
foreach ($types as $row) {
|
||||
$fake = (new SingleTask())->setType($row['type']);
|
||||
$label = $this->taskWorkflowManager->getWorkflowMetadata($fake, 'definition.name');
|
||||
$choices[$label] = $row['type'];
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of user having a task assigned.
|
||||
*
|
||||
* @return User[]
|
||||
*/
|
||||
protected function getUsersAssigneedToTask($options)
|
||||
{
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
$user = $this->tokenStorage->getToken()->getUser();
|
||||
$role = new Role(TaskVoter::SHOW);
|
||||
$centers = $this->authorizationHelper->getReachableCenters($user, $role);
|
||||
|
||||
$qb->select('DISTINCT user')
|
||||
->from(User::class, 'user')
|
||||
->join(SingleTask::class, 'task', \Doctrine\ORM\Query\Expr\Join::WITH, 'task.assignee = user')
|
||||
->join('task.person', 'person')
|
||||
->where("user.enabled = 'TRUE'")
|
||||
;
|
||||
|
||||
if (NULL !== $options['person']) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->eq('task.person', ':person'))
|
||||
->setParameter('person', $options['person'])
|
||||
;
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$circleCenterCond = $qb->expr()->orX();
|
||||
foreach ($centers as $center) {
|
||||
$circles = $this->authorizationHelper->getReachableCircles($user, $role, $center);
|
||||
// add condition about person and circle
|
||||
$circleCenterCond->add(
|
||||
$qb->expr()->andX()
|
||||
->add($qb->expr()->eq('person.center', ':center_'.$i))
|
||||
->add($qb->expr()->in('task.circle', ':circles_'.$i))
|
||||
);
|
||||
|
||||
$qb->setParameter('center_'.$i, $center)
|
||||
->setParameter('circles_'.$i, $circles)
|
||||
;
|
||||
// increase counter
|
||||
$i++;
|
||||
}
|
||||
$qb->andWhere($circleCenterCond);
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
protected function getReachablesCenters()
|
||||
{
|
||||
$user = $this->tokenStorage->getToken()->getUser();
|
||||
$role = new Role(TaskVoter::SHOW);
|
||||
|
||||
return $this->authorizationHelper->getReachableCenters($user, $role);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
@@ -290,7 +150,124 @@ class SingleTaskListType extends AbstractType
|
||||
->setAllowedTypes('add_status', ['bool'])
|
||||
->setDefined('add_type')
|
||||
->setDefault('add_type', false)
|
||||
->setAllowedTypes('add_type', ['bool'])
|
||||
;
|
||||
->setAllowedTypes('add_type', ['bool']);
|
||||
}
|
||||
|
||||
protected function getReachablesCenters()
|
||||
{
|
||||
$user = $this->tokenStorage->getToken()->getUser();
|
||||
$role = new Role(TaskVoter::SHOW);
|
||||
|
||||
return $this->authorizationHelper->getReachableCenters($user, $role);
|
||||
}
|
||||
|
||||
protected function getTaskTypesChoices($options)
|
||||
{
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
$user = $this->tokenStorage->getToken()->getUser();
|
||||
$role = new Role(TaskVoter::SHOW);
|
||||
$centers = $this->authorizationHelper->getReachableCenters($user, $role);
|
||||
|
||||
$qb->select('DISTINCT task.type AS type')
|
||||
->from(SingleTask::class, 'task')
|
||||
->join('task.person', 'person');
|
||||
|
||||
$i = 0;
|
||||
$orCenters = $qb->expr()->orX();
|
||||
|
||||
foreach ($centers as $center) {
|
||||
$circles = $this->authorizationHelper->getReachableCircles($user, $role, $center);
|
||||
|
||||
if (count($circles) > 0) {
|
||||
$andX = $qb->expr()->andX();
|
||||
$andX
|
||||
->add($qb->expr()->eq('person.center', ':center_' . $i))
|
||||
->add($qb->expr()->in('task.circle', ':circles_' . $i));
|
||||
$orCenters->add($andX);
|
||||
|
||||
$qb
|
||||
->setParameter('center_' . $i, $center)
|
||||
->setParameter('circles_' . $i, $circles);
|
||||
++$i;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 < $i) {
|
||||
$qb->where($orCenters);
|
||||
}
|
||||
|
||||
$types = $qb->getQuery()->getResult();
|
||||
|
||||
$choices = [];
|
||||
|
||||
foreach ($types as $row) {
|
||||
$fake = (new SingleTask())->setType($row['type']);
|
||||
$label = $this->taskWorkflowManager->getWorkflowMetadata($fake, 'definition.name');
|
||||
$choices[$label] = $row['type'];
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
protected function getUserChoices($options)
|
||||
{
|
||||
$users = $this->getUsersAssigneedToTask($options);
|
||||
$choices = array_combine(
|
||||
// get usernames
|
||||
array_map(function (User $user) { return $user->getUsername(); }, $users),
|
||||
// get ids
|
||||
array_map(function (User $user) { return $user->getId(); }, $users)
|
||||
);
|
||||
$choices['Unassigned'] = '_unassigned';
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of user having a task assigned.
|
||||
*
|
||||
* @param mixed $options
|
||||
*
|
||||
* @return User[]
|
||||
*/
|
||||
protected function getUsersAssigneedToTask($options)
|
||||
{
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
$user = $this->tokenStorage->getToken()->getUser();
|
||||
$role = new Role(TaskVoter::SHOW);
|
||||
$centers = $this->authorizationHelper->getReachableCenters($user, $role);
|
||||
|
||||
$qb->select('DISTINCT user')
|
||||
->from(User::class, 'user')
|
||||
->join(SingleTask::class, 'task', \Doctrine\ORM\Query\Expr\Join::WITH, 'task.assignee = user')
|
||||
->join('task.person', 'person')
|
||||
->where("user.enabled = 'TRUE'");
|
||||
|
||||
if (null !== $options['person']) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->eq('task.person', ':person'))
|
||||
->setParameter('person', $options['person']);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$circleCenterCond = $qb->expr()->orX();
|
||||
|
||||
foreach ($centers as $center) {
|
||||
$circles = $this->authorizationHelper->getReachableCircles($user, $role, $center);
|
||||
// add condition about person and circle
|
||||
$circleCenterCond->add(
|
||||
$qb->expr()->andX()
|
||||
->add($qb->expr()->eq('person.center', ':center_' . $i))
|
||||
->add($qb->expr()->in('task.circle', ':circles_' . $i))
|
||||
);
|
||||
|
||||
$qb->setParameter('center_' . $i, $center)
|
||||
->setParameter('circles_' . $i, $circles);
|
||||
// increase counter
|
||||
++$i;
|
||||
}
|
||||
$qb->andWhere($circleCenterCond);
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
}
|
||||
|
@@ -1,28 +1,37 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\TaskBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||
use Chill\MainBundle\Form\Type\DateIntervalType;
|
||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||
use Chill\MainBundle\Form\Type\UserPickerType;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
||||
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
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 Chill\MainBundle\Form\Type\UserPickerType;
|
||||
use Chill\MainBundle\Form\Type\ScopePickerType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Chill\MainBundle\Form\Type\DateIntervalType;
|
||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||
|
||||
class SingleTaskType extends AbstractType
|
||||
{
|
||||
private ParameterBagInterface $parameterBag;
|
||||
private CenterResolverDispatcherInterface $centerResolverDispatcher;
|
||||
|
||||
private ParameterBagInterface $parameterBag;
|
||||
|
||||
private ScopeResolverDispatcher $scopeResolverDispatcher;
|
||||
|
||||
public function __construct(
|
||||
@@ -40,7 +49,7 @@ class SingleTaskType extends AbstractType
|
||||
$center = null;
|
||||
$isScopeConcerned = false;
|
||||
|
||||
if (NULL !== $task = $options['data']) {
|
||||
if (null !== $task = $options['data']) {
|
||||
$center = $this->centerResolverDispatcher->resolveCenter($task);
|
||||
$isScopeConcerned = $this->scopeResolverDispatcher->isConcerned($task);
|
||||
}
|
||||
@@ -48,23 +57,23 @@ class SingleTaskType extends AbstractType
|
||||
$builder
|
||||
->add('title', TextType::class)
|
||||
->add('description', ChillTextareaType::class, [
|
||||
'required' => false
|
||||
'required' => false,
|
||||
])
|
||||
->add('assignee', UserPickerType::class, [
|
||||
'required' => false,
|
||||
'center' => $center,
|
||||
'role' => TaskVoter::SHOW,
|
||||
'role' => TaskVoter::SHOW,
|
||||
'placeholder' => 'Not assigned',
|
||||
'attr' => [ 'class' => ' select2 ']
|
||||
])
|
||||
'attr' => ['class' => ' select2 '],
|
||||
])
|
||||
->add('startDate', ChillDateType::class, [
|
||||
'required' => false
|
||||
'required' => false,
|
||||
])
|
||||
->add('endDate', ChillDateType::class, [
|
||||
'required' => false
|
||||
'required' => false,
|
||||
])
|
||||
->add('warningInterval', DateIntervalType::class, [
|
||||
'required' => false
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
if ($isScopeConcerned && $this->parameterBag->get('chill_main')['acl']['form_show_scopes']) {
|
||||
@@ -72,7 +81,7 @@ class SingleTaskType extends AbstractType
|
||||
->add('circle', ScopePickerType::class, [
|
||||
'center' => $center,
|
||||
'role' => $options['role'],
|
||||
'required' => false
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -81,7 +90,6 @@ class SingleTaskType extends AbstractType
|
||||
{
|
||||
$resolver
|
||||
->setRequired('role')
|
||||
->setAllowedTypes('role', [ Role::class, 'string' ])
|
||||
;
|
||||
->setAllowedTypes('role', [Role::class, 'string']);
|
||||
}
|
||||
}
|
||||
|
@@ -1,105 +1,98 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2018 Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* 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/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Menu;
|
||||
|
||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use Knp\Menu\MenuItem;
|
||||
use LogicException;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class MenuBuilder implements LocalMenuBuilderInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var AuthorizationCheckerInterface
|
||||
*/
|
||||
protected $authorizationChecker;
|
||||
|
||||
/**
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
public function __construct(
|
||||
AuthorizationCheckerInterface $authorizationChecker,
|
||||
TranslatorInterface $translator)
|
||||
TranslatorInterface $translator
|
||||
)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
$this->authorizationChecker = $authorizationChecker;
|
||||
}
|
||||
|
||||
|
||||
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||
public function buildAccompanyingCourseMenu($menu, $parameters)
|
||||
{
|
||||
switch($menuId) {
|
||||
case 'person':
|
||||
$this->buildPersonMenu($menu, $parameters);
|
||||
break;
|
||||
case 'accompanyingCourse':
|
||||
$this->buildAccompanyingCourseMenu($menu, $parameters);
|
||||
break;
|
||||
case 'section':
|
||||
$menu->setExtras('icons', 'tasks');
|
||||
break;
|
||||
default:
|
||||
throw new \LogicException("this menuid $menuId is not implemented");
|
||||
$course = $parameters['accompanyingCourse'];
|
||||
|
||||
if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) {
|
||||
$menu->addChild(
|
||||
$this->translator->trans('Tasks'),
|
||||
[
|
||||
'route' => 'chill_task_singletask_by-course_list',
|
||||
'routeParameters' => ['id' => $course->getId()],
|
||||
]
|
||||
)
|
||||
->setExtra('order', 400);
|
||||
}
|
||||
}
|
||||
|
||||
public function buildPersonMenu($menu, $parameters){
|
||||
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||
{
|
||||
switch ($menuId) {
|
||||
case 'person':
|
||||
$this->buildPersonMenu($menu, $parameters);
|
||||
|
||||
break;
|
||||
|
||||
case 'accompanyingCourse':
|
||||
$this->buildAccompanyingCourseMenu($menu, $parameters);
|
||||
|
||||
break;
|
||||
|
||||
case 'section':
|
||||
$menu->setExtras('icons', 'tasks');
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new LogicException("this menuid {$menuId} is not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
public function buildPersonMenu($menu, $parameters)
|
||||
{
|
||||
//var $person \Chill\PersonBundle\Entity\Person */
|
||||
$person = $parameters['person'] ?? null;
|
||||
|
||||
if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $person)) {
|
||||
$menu->addChild(
|
||||
$this->translator->trans('Tasks'), [
|
||||
$this->translator->trans('Tasks'),
|
||||
[
|
||||
'route' => 'chill_task_singletask_by-person_list',
|
||||
'routeParameters' =>
|
||||
[ 'id' => $person->getId() ]
|
||||
])
|
||||
'routeParameters' => ['id' => $person->getId()],
|
||||
]
|
||||
)
|
||||
->setExtra('order', 400);
|
||||
}
|
||||
}
|
||||
|
||||
public function buildAccompanyingCourseMenu($menu, $parameters){
|
||||
|
||||
$course = $parameters['accompanyingCourse'];
|
||||
|
||||
if ($this->authorizationChecker->isGranted(TaskVoter::SHOW, $course)) {
|
||||
$menu->addChild(
|
||||
$this->translator->trans('Tasks'), [
|
||||
'route' => 'chill_task_singletask_by-course_list',
|
||||
'routeParameters' =>
|
||||
[ 'id' => $course->getId() ]
|
||||
])
|
||||
->setExtra('order', 400);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function getMenuIds(): array
|
||||
{
|
||||
return ['person', 'accompanyingCourse'];
|
||||
|
@@ -1,47 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2018 Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* 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/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Menu;
|
||||
|
||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
class SectionMenuBuilder implements LocalMenuBuilderInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var AuthorizationCheckerInterface
|
||||
*/
|
||||
public $authorizationChecker;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
public $translator;
|
||||
|
||||
|
||||
public function __construct(
|
||||
AuthorizationCheckerInterface $authorizationChecker,
|
||||
TranslatorInterface $translator
|
||||
@@ -49,30 +34,31 @@ class SectionMenuBuilder implements LocalMenuBuilderInterface
|
||||
$this->authorizationChecker = $authorizationChecker;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
|
||||
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||
{
|
||||
if (FALSE === $this->authorizationChecker->isGranted(TaskVoter::SHOW)) {
|
||||
if (false === $this->authorizationChecker->isGranted(TaskVoter::SHOW)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$menu->addChild(
|
||||
$this->translator->trans("Tasks"),
|
||||
$this->translator->trans('Tasks'),
|
||||
[
|
||||
'route' => 'chill_task_singletask_list', [
|
||||
'routeParameters' => [
|
||||
'hide_form' => false
|
||||
]
|
||||
]])
|
||||
'routeParameters' => [
|
||||
'hide_form' => false,
|
||||
],
|
||||
], ]
|
||||
)
|
||||
->setExtras([
|
||||
'order'=> 50,
|
||||
'order' => 50,
|
||||
'icon' => 'exclamation-triangle',
|
||||
'entryclass' => 'user_menu__entry--warning-entry'
|
||||
'entryclass' => 'user_menu__entry--warning-entry',
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getMenuIds(): array
|
||||
{
|
||||
return [ 'section' ];
|
||||
return ['section'];
|
||||
}
|
||||
}
|
||||
|
@@ -1,42 +1,30 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* 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/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Menu;
|
||||
|
||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Chill\TaskBundle\Templating\UI\CountNotificationTask;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Chill\TaskBundle\Repository\SingleTaskRepository;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use Chill\TaskBundle\Templating\UI\CountNotificationTask;
|
||||
use Knp\Menu\MenuItem;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class UserMenuBuilder implements LocalMenuBuilderInterface
|
||||
{
|
||||
/**
|
||||
* @var AuthorizationCheckerInterface
|
||||
*/
|
||||
public $authorizationChecker;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var CountNotificationTask
|
||||
*/
|
||||
public $counter;
|
||||
@@ -47,17 +35,10 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
|
||||
public $tokenStorage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
public $translator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var AuthorizationCheckerInterface
|
||||
*/
|
||||
public $authorizationChecker;
|
||||
|
||||
public function __construct(
|
||||
CountNotificationTask $counter,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
@@ -70,14 +51,9 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
|
||||
$this->authorizationChecker = $authorizationChecker;
|
||||
}
|
||||
|
||||
public static function getMenuIds(): array
|
||||
{
|
||||
return [ 'user' ];
|
||||
}
|
||||
|
||||
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||
{
|
||||
if (FALSE === $this->authorizationChecker->isGranted(TaskVoter::SHOW)) {
|
||||
if (false === $this->authorizationChecker->isGranted(TaskVoter::SHOW)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -85,7 +61,7 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
|
||||
$ended = $this->counter->countNotificationEnded($user);
|
||||
$warning = $this->counter->countNotificationWarning($user);
|
||||
|
||||
if ($ended > 0) {
|
||||
if (0 < $ended) {
|
||||
$this->addItemInMenu(
|
||||
$menu,
|
||||
'%number% tasks over deadline',
|
||||
@@ -96,7 +72,7 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
|
||||
);
|
||||
}
|
||||
|
||||
if ($warning > 0) {
|
||||
if (0 < $warning) {
|
||||
$this->addItemInMenu(
|
||||
$menu,
|
||||
'%number% tasks near deadline',
|
||||
@@ -107,19 +83,23 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
|
||||
);
|
||||
}
|
||||
|
||||
$menu->addChild("My tasks", [
|
||||
'route' => 'chill_task_singletask_my_tasks'
|
||||
])
|
||||
$menu->addChild('My tasks', [
|
||||
'route' => 'chill_task_singletask_my_tasks',
|
||||
])
|
||||
->setExtras([
|
||||
'order' => -10,
|
||||
'icon' => 'tasks'
|
||||
'icon' => 'tasks',
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getMenuIds(): array
|
||||
{
|
||||
return ['user'];
|
||||
}
|
||||
|
||||
protected function addItemInMenu(MenuItem $menu, $message, $number, $order, array $states = [], array $status = [])
|
||||
{
|
||||
if ($number > 0) {
|
||||
if (0 < $number) {
|
||||
$menu->addChild(
|
||||
$this->translator->transChoice($message, $number),
|
||||
[
|
||||
@@ -128,15 +108,16 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
|
||||
'f' => [
|
||||
'checkboxes' => [
|
||||
'states' => $states,
|
||||
'status' => $status
|
||||
]
|
||||
]
|
||||
'status' => $status,
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
])
|
||||
)
|
||||
->setExtras([
|
||||
'order'=> $order,
|
||||
'order' => $order,
|
||||
'icon' => 'exclamation-triangle',
|
||||
'entryclass' => 'user_menu__entry--warning-entry'
|
||||
'entryclass' => 'user_menu__entry--warning-entry',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Repository;
|
||||
|
||||
/**
|
||||
* AbstractTaskRepository
|
||||
* AbstractTaskRepository.
|
||||
*
|
||||
* This class was generated by the Doctrine ORM. Add your own custom
|
||||
* repository methods below.
|
||||
|
@@ -1,9 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Repository;
|
||||
|
||||
/**
|
||||
* RecurringTaskRepository
|
||||
* RecurringTaskRepository.
|
||||
*
|
||||
* This class was generated by the Doctrine ORM. Add your own custom
|
||||
* repository methods below.
|
||||
|
@@ -1,5 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\TaskBundle\Repository;
|
||||
@@ -10,17 +17,24 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\TaskBundle\Entity\SingleTask;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use LogicException;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use function substr;
|
||||
|
||||
final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepositoryInterface
|
||||
{
|
||||
private AuthorizationHelperInterface $authorizationHelper;
|
||||
private EntityManagerInterface $em;
|
||||
private Security $security;
|
||||
|
||||
private CenterResolverDispatcherInterface $centerResolverDispatcher;
|
||||
|
||||
private EntityManagerInterface $em;
|
||||
|
||||
private Security $security;
|
||||
|
||||
public function __construct(
|
||||
CenterResolverDispatcherInterface $centerResolverDispatcher,
|
||||
EntityManagerInterface $em,
|
||||
@@ -33,41 +47,150 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
}
|
||||
|
||||
public function findByCurrentUsersTasks(
|
||||
public function buildBaseQuery(
|
||||
?string $pattern = null,
|
||||
?array $flags = [],
|
||||
?int $start = 0,
|
||||
?int $limit = 50,
|
||||
?array $orderBy = []
|
||||
): array {
|
||||
$qb = $this->buildQueryMyTasks($pattern, $flags);
|
||||
?array $flags = []
|
||||
): QueryBuilder {
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
$qb
|
||||
->from(SingleTask::class, 't');
|
||||
|
||||
return $this->getResult($qb, $start, $limit, $orderBy);
|
||||
if (!empty($pattern)) {
|
||||
$qb->andWhere($qb->expr()->like('LOWER(UNACCENT(t.title))', 'LOWER(UNACCENT(:pattern))'))
|
||||
->setParameter('pattern', '%' . $pattern . '%');
|
||||
}
|
||||
|
||||
if (count($flags) > 0) {
|
||||
$orXDate = $qb->expr()->orX();
|
||||
$orXState = $qb->expr()->orX();
|
||||
$now = new DateTime();
|
||||
|
||||
foreach ($flags as $key => $flag) {
|
||||
switch ($flag) {
|
||||
case 'no-alert':
|
||||
$orXDate
|
||||
->add(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('t.endDate'),
|
||||
$qb->expr()->gte('t.endDate - COALESCE(t.warningInterval, :intervalBlank)', ':now')
|
||||
)
|
||||
);
|
||||
$qb
|
||||
->setParameter('intervalBlank', new DateInterval('P0D'))
|
||||
->setParameter('now', $now);
|
||||
|
||||
break;
|
||||
|
||||
case 'warning':
|
||||
$orXDate
|
||||
->add(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->not($qb->expr()->isNull('t.endDate')),
|
||||
$qb->expr()->not($qb->expr()->isNull('t.warningInterval')),
|
||||
$qb->expr()->lte('t.endDate - t.warningInterval', ':now'),
|
||||
$qb->expr()->gt('t.endDate', ':now')
|
||||
)
|
||||
);
|
||||
$qb
|
||||
->setParameter('now', $now);
|
||||
|
||||
break;
|
||||
|
||||
case 'alert':
|
||||
$orXDate
|
||||
->add(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->not($qb->expr()->isNull('t.endDate')),
|
||||
$qb->expr()->lte('t.endDate', ':now')
|
||||
)
|
||||
);
|
||||
$qb
|
||||
->setParameter('now', $now);
|
||||
|
||||
break;
|
||||
|
||||
case 'state_new':
|
||||
$orXState
|
||||
->add(
|
||||
'JSONB_ARRAY_LENGTH(t.currentStates) = 0'
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case substr($flag, 0, 6) === 'state_':
|
||||
$state = substr($flag, 6);
|
||||
$orXState
|
||||
->add(
|
||||
"JSONB_EXISTS_IN_ARRAY(t.currentStates, :state_{$key}) = 'TRUE'"
|
||||
);
|
||||
$qb->setParameter("state_{$key}", $state);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new LogicException("this flag is not supported: {$flag}");
|
||||
}
|
||||
}
|
||||
|
||||
if ($orXDate->count() > 0) {
|
||||
$qb->andWhere($orXDate);
|
||||
}
|
||||
|
||||
if ($orXState->count() > 0) {
|
||||
$qb->andWhere($orXState);
|
||||
}
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function countByCurrentUsersTasks(
|
||||
public function buildQueryByCourse(
|
||||
AccompanyingPeriod $course,
|
||||
?string $pattern = null,
|
||||
?array $flags = []
|
||||
): QueryBuilder {
|
||||
$qb = $this->buildBaseQuery($pattern, $flags);
|
||||
|
||||
return $qb
|
||||
->andWhere($qb->expr()->eq('t.course', ':course'))
|
||||
->setParameter('course', $course);
|
||||
}
|
||||
|
||||
public function buildQueryByPerson(
|
||||
Person $person,
|
||||
?string $pattern = null,
|
||||
?array $flags = []
|
||||
): QueryBuilder {
|
||||
$qb = $this->buildBaseQuery($pattern, $flags);
|
||||
|
||||
return $qb
|
||||
->andWhere($qb->expr()->eq('t.person', ':person'))
|
||||
->setParameter('person', $person);
|
||||
}
|
||||
|
||||
public function buildQueryMyTasks(
|
||||
?string $pattern = null,
|
||||
?array $flags = []
|
||||
): QueryBuilder {
|
||||
$qb = $this->buildBaseQuery($pattern, $flags);
|
||||
|
||||
return $qb
|
||||
->andWhere($qb->expr()->eq('t.assignee', ':user'))
|
||||
->setParameter('user', $this->security->getUser());
|
||||
}
|
||||
|
||||
public function countByAllViewable(
|
||||
?string $pattern = null,
|
||||
?array $flags = []
|
||||
): int {
|
||||
return $this->buildQueryMyTasks($pattern, $flags)
|
||||
$qb = $this->buildBaseQuery($pattern, $flags);
|
||||
|
||||
return $this
|
||||
->addACLGlobal($qb)
|
||||
->select('COUNT(t)')
|
||||
->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function findByCourse(
|
||||
AccompanyingPeriod $course,
|
||||
?string $pattern = null,
|
||||
?array $flags = [],
|
||||
?int $start = 0,
|
||||
?int $limit = 50,
|
||||
?array $orderBy = []
|
||||
): array {
|
||||
$qb = $this->buildQueryByCourse($course, $pattern, $flags);
|
||||
$qb = $this->addACL($qb, $course);
|
||||
|
||||
return $this->getResult($qb, $start, $limit, $orderBy);
|
||||
}
|
||||
|
||||
public function countByCourse(
|
||||
AccompanyingPeriod $course,
|
||||
?string $pattern = null,
|
||||
@@ -81,18 +204,13 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository
|
||||
->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function findByPerson(
|
||||
Person $person,
|
||||
public function countByCurrentUsersTasks(
|
||||
?string $pattern = null,
|
||||
?array $flags = [],
|
||||
?int $start = 0,
|
||||
?int $limit = 50,
|
||||
?array $orderBy = []
|
||||
): array {
|
||||
$qb = $this->buildQueryByPerson($person, $pattern, $flags);
|
||||
$qb = $this->addACL($qb, $person);
|
||||
|
||||
return $this->getResult($qb, $start, $limit, $orderBy);
|
||||
?array $flags = []
|
||||
): int {
|
||||
return $this->buildQueryMyTasks($pattern, $flags)
|
||||
->select('COUNT(t)')
|
||||
->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function countByPerson(
|
||||
@@ -108,18 +226,6 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository
|
||||
->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function countByAllViewable(
|
||||
?string $pattern = null,
|
||||
?array $flags = []
|
||||
): int {
|
||||
$qb = $this->buildBaseQuery($pattern, $flags);
|
||||
|
||||
return $this
|
||||
->addACLGlobal($qb)
|
||||
->select('COUNT(t)')
|
||||
->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function findByAllViewable(
|
||||
?string $pattern = null,
|
||||
?array $flags = [],
|
||||
@@ -133,42 +239,44 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository
|
||||
return $this->getResult($qb, $start, $limit, $orderBy);
|
||||
}
|
||||
|
||||
public function buildQueryByCourse(
|
||||
public function findByCourse(
|
||||
AccompanyingPeriod $course,
|
||||
?string $pattern = null,
|
||||
?array $flags = []
|
||||
) : QueryBuilder {
|
||||
$qb = $this->buildBaseQuery($pattern, $flags);
|
||||
?array $flags = [],
|
||||
?int $start = 0,
|
||||
?int $limit = 50,
|
||||
?array $orderBy = []
|
||||
): array {
|
||||
$qb = $this->buildQueryByCourse($course, $pattern, $flags);
|
||||
$qb = $this->addACL($qb, $course);
|
||||
|
||||
return $qb
|
||||
->andWhere($qb->expr()->eq('t.course', ':course'))
|
||||
->setParameter('course', $course)
|
||||
;
|
||||
return $this->getResult($qb, $start, $limit, $orderBy);
|
||||
}
|
||||
|
||||
public function buildQueryByPerson(
|
||||
Person $person,
|
||||
?string $pattern = null,
|
||||
?array $flags = []
|
||||
): QueryBuilder
|
||||
{
|
||||
$qb = $this->buildBaseQuery($pattern, $flags);
|
||||
|
||||
return $qb
|
||||
->andWhere($qb->expr()->eq('t.person', ':person'))
|
||||
->setParameter('person', $person);
|
||||
}
|
||||
|
||||
public function buildQueryMyTasks(
|
||||
public function findByCurrentUsersTasks(
|
||||
?string $pattern = null,
|
||||
?array $flags = []
|
||||
): QueryBuilder {
|
||||
$qb = $this->buildBaseQuery($pattern, $flags);
|
||||
?array $flags = [],
|
||||
?int $start = 0,
|
||||
?int $limit = 50,
|
||||
?array $orderBy = []
|
||||
): array {
|
||||
$qb = $this->buildQueryMyTasks($pattern, $flags);
|
||||
|
||||
return $qb
|
||||
->andWhere($qb->expr()->eq('t.assignee', ':user'))
|
||||
->setParameter('user', $this->security->getUser())
|
||||
;
|
||||
return $this->getResult($qb, $start, $limit, $orderBy);
|
||||
}
|
||||
|
||||
public function findByPerson(
|
||||
Person $person,
|
||||
?string $pattern = null,
|
||||
?array $flags = [],
|
||||
?int $start = 0,
|
||||
?int $limit = 50,
|
||||
?array $orderBy = []
|
||||
): array {
|
||||
$qb = $this->buildQueryByPerson($person, $pattern, $flags);
|
||||
$qb = $this->addACL($qb, $person);
|
||||
|
||||
return $this->getResult($qb, $start, $limit, $orderBy);
|
||||
}
|
||||
|
||||
public function getResult(
|
||||
@@ -181,11 +289,10 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository
|
||||
|
||||
$qb
|
||||
->setFirstResult($start)
|
||||
->setMaxResults($limit)
|
||||
;
|
||||
->setMaxResults($limit);
|
||||
|
||||
foreach ($orderBy as $field => $direction) {
|
||||
$qb->addOrderBy('t.'.$field, $direction);
|
||||
$qb->addOrderBy('t.' . $field, $direction);
|
||||
}
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
@@ -195,11 +302,14 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository
|
||||
QueryBuilder $qb,
|
||||
$entity
|
||||
): QueryBuilder {
|
||||
$scopes = $this->authorizationHelper->getReachableScopes($this->security->getUser(),
|
||||
TaskVoter::SHOW, $this->centerResolverDispatcher->resolveCenter($entity));
|
||||
$scopes = $this->authorizationHelper->getReachableScopes(
|
||||
$this->security->getUser(),
|
||||
TaskVoter::SHOW,
|
||||
$this->centerResolverDispatcher->resolveCenter($entity)
|
||||
);
|
||||
|
||||
return $qb->andWhere($qb->expr()->in('t.circle', ':scopes'))
|
||||
->setParameter('scopes', $scopes);
|
||||
return $qb->andWhere($qb->expr()->in('t.circle', ':scopes'))
|
||||
->setParameter('scopes', $scopes);
|
||||
}
|
||||
|
||||
private function addACLGlobal(
|
||||
@@ -217,121 +327,35 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository
|
||||
$qb->leftJoin('t.person', 'person')
|
||||
->leftJoin('t.course', 'course')
|
||||
->leftJoin('course.participations', 'participation')
|
||||
->leftJoin('participation.person', 'person_p')
|
||||
;
|
||||
->leftJoin('participation.person', 'person_p');
|
||||
$qb->distinct(true);
|
||||
|
||||
$k = 0;
|
||||
$orX = $qb->expr()->orX();
|
||||
|
||||
foreach ($allowedCenters as $center) {
|
||||
$allowedScopes = $this->authorizationHelper->getReachableScopes($this->security->getUser(),
|
||||
TaskVoter::SHOW, $center);
|
||||
$allowedScopes = $this->authorizationHelper->getReachableScopes(
|
||||
$this->security->getUser(),
|
||||
TaskVoter::SHOW,
|
||||
$center
|
||||
);
|
||||
|
||||
$and = $qb->expr()->andX(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->eq('person.center', ':center_'.$k),
|
||||
$qb->expr()->eq('person_p.center', ':center_'.$k)
|
||||
$qb->expr()->eq('person.center', ':center_' . $k),
|
||||
$qb->expr()->eq('person_p.center', ':center_' . $k)
|
||||
),
|
||||
$qb->expr()->in('t.circle', ':scopes_'.$k)
|
||||
$qb->expr()->in('t.circle', ':scopes_' . $k)
|
||||
);
|
||||
$qb
|
||||
->setParameter('center_'.$k, $center)
|
||||
->setParameter('scopes_'.$k, $allowedScopes);
|
||||
->setParameter('center_' . $k, $center)
|
||||
->setParameter('scopes_' . $k, $allowedScopes);
|
||||
$orX->add($and);
|
||||
|
||||
$k++;
|
||||
++$k;
|
||||
}
|
||||
$qb->andWhere($orX);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function buildBaseQuery (
|
||||
?string $pattern = null,
|
||||
?array $flags = []
|
||||
): QueryBuilder {
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
$qb
|
||||
->from(SingleTask::class, 't')
|
||||
;
|
||||
|
||||
if (!empty($pattern)) {
|
||||
$qb->andWhere($qb->expr()->like('LOWER(UNACCENT(t.title))', 'LOWER(UNACCENT(:pattern))'))
|
||||
->setParameter('pattern', '%'.$pattern.'%')
|
||||
;
|
||||
}
|
||||
|
||||
if (count($flags) > 0) {
|
||||
$orXDate = $qb->expr()->orX();
|
||||
$orXState = $qb->expr()->orX();
|
||||
$now = new \DateTime();
|
||||
|
||||
foreach ($flags as $key => $flag) {
|
||||
switch ($flag) {
|
||||
case 'no-alert':
|
||||
$orXDate
|
||||
->add(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('t.endDate'),
|
||||
$qb->expr()->gte('t.endDate - COALESCE(t.warningInterval, :intervalBlank)', ':now')
|
||||
)
|
||||
);
|
||||
$qb
|
||||
->setParameter('intervalBlank', new \DateInterval('P0D'))
|
||||
->setParameter('now', $now);
|
||||
break;
|
||||
case 'warning':
|
||||
$orXDate
|
||||
->add(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->not($qb->expr()->isNull('t.endDate')),
|
||||
$qb->expr()->not($qb->expr()->isNull('t.warningInterval')),
|
||||
$qb->expr()->lte('t.endDate - t.warningInterval', ':now'),
|
||||
$qb->expr()->gt('t.endDate', ':now')
|
||||
)
|
||||
);
|
||||
$qb
|
||||
->setParameter('now', $now);
|
||||
break;
|
||||
case 'alert':
|
||||
$orXDate
|
||||
->add(
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->not($qb->expr()->isNull('t.endDate')),
|
||||
$qb->expr()->lte('t.endDate', ':now')
|
||||
)
|
||||
);
|
||||
$qb
|
||||
->setParameter('now', $now);
|
||||
break;
|
||||
case 'state_new':
|
||||
$orXState
|
||||
->add(
|
||||
"JSONB_ARRAY_LENGTH(t.currentStates) = 0"
|
||||
);
|
||||
break;
|
||||
case \substr($flag, 0, 6) === 'state_':
|
||||
$state = \substr($flag, 6);
|
||||
$orXState
|
||||
->add(
|
||||
"JSONB_EXISTS_IN_ARRAY(t.currentStates, :state_$key) = 'TRUE'"
|
||||
);
|
||||
$qb->setParameter("state_$key", $state);
|
||||
break;
|
||||
default:
|
||||
throw new \LogicException("this flag is not supported: $flag");
|
||||
}
|
||||
}
|
||||
|
||||
if ($orXDate->count() > 0) {
|
||||
$qb->andWhere($orXDate);
|
||||
}
|
||||
if ($orXState->count() > 0) {
|
||||
$qb->andWhere($orXState);
|
||||
}
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,5 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Repository;
|
||||
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
@@ -7,18 +14,10 @@ use Chill\PersonBundle\Entity\Person;
|
||||
|
||||
interface SingleTaskAclAwareRepositoryInterface
|
||||
{
|
||||
public function findByCurrentUsersTasks(?string $pattern = null, ?array $flags = [], ?int $start = 0, ?int $limit = 50, ?array $orderBy = []): array;
|
||||
|
||||
public function countByCurrentUsersTasks(?string $pattern = null, ?array $flags = []): int;
|
||||
|
||||
public function findByCourse(
|
||||
AccompanyingPeriod $course,
|
||||
public function countByAllViewable(
|
||||
?string $pattern = null,
|
||||
?array $flags = [],
|
||||
?int $start = 0,
|
||||
?int $limit = 50,
|
||||
?array $orderBy = []
|
||||
): array;
|
||||
?array $flags = []
|
||||
): int;
|
||||
|
||||
public function countByCourse(
|
||||
AccompanyingPeriod $course,
|
||||
@@ -26,14 +25,7 @@ interface SingleTaskAclAwareRepositoryInterface
|
||||
?array $flags = []
|
||||
): int;
|
||||
|
||||
public function findByPerson(
|
||||
Person $person,
|
||||
?string $pattern = null,
|
||||
?array $flags = [],
|
||||
?int $start = 0,
|
||||
?int $limit = 50,
|
||||
?array $orderBy = []
|
||||
): array;
|
||||
public function countByCurrentUsersTasks(?string $pattern = null, ?array $flags = []): int;
|
||||
|
||||
public function countByPerson(
|
||||
Person $person,
|
||||
@@ -41,11 +33,6 @@ interface SingleTaskAclAwareRepositoryInterface
|
||||
?array $flags = []
|
||||
): int;
|
||||
|
||||
public function countByAllViewable(
|
||||
?string $pattern = null,
|
||||
?array $flags = []
|
||||
): int;
|
||||
|
||||
public function findByAllViewable(
|
||||
?string $pattern = null,
|
||||
?array $flags = [],
|
||||
@@ -53,4 +40,24 @@ interface SingleTaskAclAwareRepositoryInterface
|
||||
?int $limit = 50,
|
||||
?array $orderBy = []
|
||||
): array;
|
||||
|
||||
public function findByCourse(
|
||||
AccompanyingPeriod $course,
|
||||
?string $pattern = null,
|
||||
?array $flags = [],
|
||||
?int $start = 0,
|
||||
?int $limit = 50,
|
||||
?array $orderBy = []
|
||||
): array;
|
||||
|
||||
public function findByCurrentUsersTasks(?string $pattern = null, ?array $flags = [], ?int $start = 0, ?int $limit = 50, ?array $orderBy = []): array;
|
||||
|
||||
public function findByPerson(
|
||||
Person $person,
|
||||
?string $pattern = null,
|
||||
?array $flags = [],
|
||||
?int $start = 0,
|
||||
?int $limit = 50,
|
||||
?array $orderBy = []
|
||||
): array;
|
||||
}
|
||||
|
@@ -1,58 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use DateTime;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use LogicException;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use UnexpectedValueException;
|
||||
use function array_key_exists;
|
||||
|
||||
/**
|
||||
* Class SingleTaskRepository
|
||||
*
|
||||
* @package Chill\TaskBundle\Repository
|
||||
* Class SingleTaskRepository.
|
||||
*/
|
||||
class SingleTaskRepository extends EntityRepository
|
||||
{
|
||||
public const DATE_STATUS_CURRENT = 'current';
|
||||
|
||||
const DATE_STATUS_ENDED = 'ended';
|
||||
const DATE_STATUS_WARNING = 'warning';
|
||||
const DATE_STATUS_CURRENT = 'current';
|
||||
const DATE_STATUS_NOT_STARTED = 'not_started';
|
||||
public const DATE_STATUS_ENDED = 'ended';
|
||||
|
||||
const DATE_STATUSES = [
|
||||
public const DATE_STATUS_NOT_STARTED = 'not_started';
|
||||
|
||||
public const DATE_STATUS_WARNING = 'warning';
|
||||
|
||||
public const DATE_STATUSES = [
|
||||
self::DATE_STATUS_ENDED,
|
||||
self::DATE_STATUS_WARNING,
|
||||
self::DATE_STATUS_CURRENT,
|
||||
self::DATE_STATUS_NOT_STARTED
|
||||
self::DATE_STATUS_NOT_STARTED,
|
||||
];
|
||||
|
||||
/**
|
||||
*
|
||||
* @var AuthorizationHelper
|
||||
*/
|
||||
protected $authorizationHelper;
|
||||
|
||||
public function setAuthorizationHelper(AuthorizationHelper $authorizationHelper)
|
||||
{
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the tasks for given parameters.
|
||||
*
|
||||
* The parameters are describe in @see SingleTaskRepository::filterByParameters.
|
||||
*
|
||||
* @see SingleTaskRepository::filterByParameters
|
||||
*
|
||||
* @param array $params
|
||||
* @param User $currentUser
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countByParameters($params, User $currentUser = null)
|
||||
public function countByParameters($params, ?User $currentUser = null)
|
||||
{
|
||||
$qb = $this->createQueryBuilder('st')
|
||||
->select('COUNT(st)');
|
||||
@@ -61,8 +68,7 @@ class SingleTaskRepository extends EntityRepository
|
||||
|
||||
return (int) $qb
|
||||
->getQuery()
|
||||
->getSingleScalarResult()
|
||||
;
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,9 +86,7 @@ class SingleTaskRepository extends EntityRepository
|
||||
* - `types`: string[] an array of task types
|
||||
*
|
||||
* @param type $params
|
||||
* @param User $currentUser
|
||||
* @param int $firstResult
|
||||
* @param int $maxResults
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
public function findByParameters($params, User $currentUser, int $firstResult = 0, int $maxResults = 50)
|
||||
@@ -93,68 +97,16 @@ class SingleTaskRepository extends EntityRepository
|
||||
|
||||
$qb
|
||||
->setMaxResults($maxResults)
|
||||
->setFirstResult($firstResult)
|
||||
;
|
||||
->setFirstResult($firstResult);
|
||||
|
||||
return $qb
|
||||
->getQuery()
|
||||
->getResult()
|
||||
;
|
||||
->getResult();
|
||||
}
|
||||
|
||||
protected function buildQuery(QueryBuilder $qb, $params, User $currentUser = null)
|
||||
public function setAuthorizationHelper(AuthorizationHelper $authorizationHelper)
|
||||
{
|
||||
if (NULL !== $currentUser) {
|
||||
$this->buildACLQuery($qb, $currentUser);
|
||||
}
|
||||
|
||||
if (\array_key_exists('person', $params) and !empty($params['person'])) {
|
||||
$qb->andWhere($qb->expr()->eq('st.person', ':person'));
|
||||
$qb->setParameter('person', $params['person']);
|
||||
} elseif (\array_key_exists('center', $params)) {
|
||||
if ($params['center'] instanceof Center) {
|
||||
$qb->join('st.person', 'person');
|
||||
$qb->andWhere($qb->expr()->eq('person.center', ':center'));
|
||||
$qb->setParameter('center', $params['center']);
|
||||
} else {
|
||||
throw new \UnexpectedValueException("params 'center' should be an instance of ".Center::class);
|
||||
}
|
||||
}
|
||||
|
||||
if (\array_key_exists('unassigned', $params) and $params['unassigned'] === true) {
|
||||
if (\array_key_exists('user', $params) and !empty($params['user'])) {
|
||||
throw new \UnexpectedValueException("You should not require for "
|
||||
. "unassigned tasks and tasks assigned to some user.");
|
||||
}
|
||||
|
||||
$qb->andWhere($qb->expr()->isNull('st.assignee'));
|
||||
}
|
||||
|
||||
if (\array_key_exists('user', $params) and !empty($params['user'])) {
|
||||
$qb->andWhere($qb->expr()->eq('st.assignee', ':user'));
|
||||
$qb->setParameter('user', $params['user']);
|
||||
}
|
||||
|
||||
if (\array_key_exists('scope', $params) and !empty($params['scope'])) {
|
||||
$qb->andWhere($qb->expr()->eq('st.circle', ':scope'));
|
||||
$qb->setParameter('scope', $params['scope']);
|
||||
}
|
||||
|
||||
if (\array_key_exists('types', $params) && $params['types'] !== NULL) {
|
||||
if (count($params['types']) > 0) {
|
||||
$qb->andWhere($qb->expr()->in('st.type', ':types'));
|
||||
$qb->setParameter('types', $params['types']);
|
||||
}
|
||||
}
|
||||
|
||||
if (\array_key_exists('date_status', $params) and !empty($params['date_status'])) {
|
||||
$this->addTypeFilter($qb, $params);
|
||||
}
|
||||
|
||||
if (\array_key_exists('is_closed', $params)) {
|
||||
$qb->andWhere($this->buildIsClosed($qb, !$params['is_closed']));
|
||||
}
|
||||
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
}
|
||||
|
||||
protected function addTypeFilter(QueryBuilder $qb, $params)
|
||||
@@ -164,15 +116,15 @@ class SingleTaskRepository extends EntityRepository
|
||||
switch ($params['date_status']) {
|
||||
case self::DATE_STATUS_ENDED:
|
||||
$andWhere
|
||||
->add($this->buildNowIsAfterEndDate($qb))
|
||||
;
|
||||
->add($this->buildNowIsAfterEndDate($qb));
|
||||
|
||||
break;
|
||||
|
||||
case self::DATE_STATUS_WARNING:
|
||||
$andWhere
|
||||
->add($this->buildNowIsAfterEndDate($qb, true))
|
||||
->add($this->buildNowIsAfterWarningDate($qb))
|
||||
;
|
||||
->add($this->buildNowIsAfterWarningDate($qb));
|
||||
|
||||
break;
|
||||
|
||||
case self::DATE_STATUS_CURRENT:
|
||||
@@ -180,125 +132,174 @@ class SingleTaskRepository extends EntityRepository
|
||||
$andWhere
|
||||
->add($this->buildNowIsAfterEndDate($qb, true))
|
||||
->add($this->buildNowIsAfterWarningDate($qb, true))
|
||||
->add($this->buildNowIsAfterStartDate($qb, false))
|
||||
;
|
||||
->add($this->buildNowIsAfterStartDate($qb, false));
|
||||
|
||||
break;
|
||||
|
||||
case self::DATE_STATUS_NOT_STARTED:
|
||||
$andWhere
|
||||
->add($this->buildNowIsAfterEndDate($qb, true))
|
||||
->add($this->buildNowIsAfterWarningDate($qb, true))
|
||||
->add($this->buildNowIsAfterStartDate($qb, true))
|
||||
;
|
||||
->add($this->buildNowIsAfterStartDate($qb, true));
|
||||
}
|
||||
$qb->setParameter('now', new \DateTime('today'), Types::DATE_MUTABLE);
|
||||
$qb->setParameter('now', new DateTime('today'), Types::DATE_MUTABLE);
|
||||
$qb->andWhere($andWhere);
|
||||
}
|
||||
|
||||
private function buildNowIsAfterEndDate(QueryBuilder $qb, $negative = false)
|
||||
{
|
||||
if ($negative === false) {
|
||||
return $qb->expr()->andX()
|
||||
->add($qb->expr()->isNotNull('st.endDate'))
|
||||
->add($qb->expr()->lte('st.endDate', ':now'))
|
||||
;
|
||||
} else {
|
||||
return $qb->expr()->orX()
|
||||
->add(
|
||||
$qb->expr()->andX()
|
||||
->add($qb->expr()->isNotNull('st.endDate'))
|
||||
->add($qb->expr()->gt('st.endDate', ':now'))
|
||||
)
|
||||
->add($qb->expr()->isNull('st.endDate'))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
private function buildNowIsAfterWarningDate(QueryBuilder $qb, bool $negative = false)
|
||||
{
|
||||
if ($negative === false) {
|
||||
return $qb->expr()->andX()
|
||||
->add($qb->expr()->lte(
|
||||
$qb->expr()->diff('st.endDate', 'st.warningInterval'), ':now'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return $qb->expr()->orX()
|
||||
->add(
|
||||
$qb->expr()->andX()
|
||||
->add($qb->expr()->isNotNull('st.endDate'))
|
||||
->add($qb->expr()->isNotNull('st.warningInterval'))
|
||||
->add($qb->expr()->gt(
|
||||
$qb->expr()->diff('st.endDate', 'st.warningInterval'),
|
||||
':now'
|
||||
)
|
||||
)
|
||||
)
|
||||
->add($qb->expr()->isNull('st.endDate'))
|
||||
->add($qb->expr()->isNull('st.warningInterval'))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
private function buildNowIsAfterStartDate(QueryBuilder $qb, bool $negative = false)
|
||||
{
|
||||
if ($negative === false) {
|
||||
return $qb->expr()->orX()
|
||||
->add($qb->expr()->lte('st.startDate', ':now'))
|
||||
->add($qb->expr()->isNull('st.startDate'))
|
||||
;
|
||||
} else {
|
||||
return
|
||||
$qb->expr()->andX()
|
||||
->add($qb->expr()->gt('st.startDate', ':now'))
|
||||
->add($qb->expr()->isNotNull('st.startDate'))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
private function buildIsClosed(QueryBuilder $qb, bool $negative = false)
|
||||
{
|
||||
if ($negative === false) {
|
||||
return $qb->expr()->eq('st.closed', "'TRUE'");
|
||||
} else {
|
||||
return $qb->expr()->eq('st.closed', "'FALSE'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function buildACLQuery(QueryBuilder $qb, User $currentUser)
|
||||
{
|
||||
if (NULL === $this->authorizationHelper) {
|
||||
throw new \LogicException("Injecting the authorization helper is "
|
||||
. "required to run this query. Please use dependency injection "
|
||||
. "to initialize this repository or use the method "
|
||||
. "`setAuthorizationHelper`");
|
||||
if (null === $this->authorizationHelper) {
|
||||
throw new LogicException('Injecting the authorization helper is '
|
||||
. 'required to run this query. Please use dependency injection '
|
||||
. 'to initialize this repository or use the method '
|
||||
. '`setAuthorizationHelper`');
|
||||
}
|
||||
|
||||
$role = new Role(TaskVoter::SHOW);
|
||||
$qb->join('st.person', 'p');
|
||||
|
||||
$centers = $this->authorizationHelper
|
||||
->getReachableCenters($currentUser, $role)
|
||||
;
|
||||
->getReachableCenters($currentUser, $role);
|
||||
|
||||
$i = 0;
|
||||
$where = $qb->expr()->orX();
|
||||
|
||||
foreach($centers as $center) {
|
||||
foreach ($centers as $center) {
|
||||
$circles = $this->authorizationHelper
|
||||
->getReachableCircles($currentUser, $role, $center);
|
||||
|
||||
$centerWhere = $qb->expr()->andX();
|
||||
|
||||
$centerWhere->add($qb->expr()->eq('p.center', ':center_'.$i));
|
||||
$qb->setParameter('center_'.$i, $center);
|
||||
$centerWhere->add($qb->expr()->in('st.circle', ':circles_'.$i));
|
||||
$qb->setParameter('circles_'.$i, $circles);
|
||||
$centerWhere->add($qb->expr()->eq('p.center', ':center_' . $i));
|
||||
$qb->setParameter('center_' . $i, $center);
|
||||
$centerWhere->add($qb->expr()->in('st.circle', ':circles_' . $i));
|
||||
$qb->setParameter('circles_' . $i, $circles);
|
||||
$where->add($centerWhere);
|
||||
$i ++;
|
||||
++$i;
|
||||
}
|
||||
|
||||
$qb->where($where);
|
||||
}
|
||||
|
||||
protected function buildQuery(QueryBuilder $qb, $params, ?User $currentUser = null)
|
||||
{
|
||||
if (null !== $currentUser) {
|
||||
$this->buildACLQuery($qb, $currentUser);
|
||||
}
|
||||
|
||||
if (array_key_exists('person', $params) and !empty($params['person'])) {
|
||||
$qb->andWhere($qb->expr()->eq('st.person', ':person'));
|
||||
$qb->setParameter('person', $params['person']);
|
||||
} elseif (array_key_exists('center', $params)) {
|
||||
if ($params['center'] instanceof Center) {
|
||||
$qb->join('st.person', 'person');
|
||||
$qb->andWhere($qb->expr()->eq('person.center', ':center'));
|
||||
$qb->setParameter('center', $params['center']);
|
||||
} else {
|
||||
throw new UnexpectedValueException("params 'center' should be an instance of " . Center::class);
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('unassigned', $params) and true === $params['unassigned']) {
|
||||
if (array_key_exists('user', $params) and !empty($params['user'])) {
|
||||
throw new UnexpectedValueException('You should not require for '
|
||||
. 'unassigned tasks and tasks assigned to some user.');
|
||||
}
|
||||
|
||||
$qb->andWhere($qb->expr()->isNull('st.assignee'));
|
||||
}
|
||||
|
||||
if (array_key_exists('user', $params) and !empty($params['user'])) {
|
||||
$qb->andWhere($qb->expr()->eq('st.assignee', ':user'));
|
||||
$qb->setParameter('user', $params['user']);
|
||||
}
|
||||
|
||||
if (array_key_exists('scope', $params) and !empty($params['scope'])) {
|
||||
$qb->andWhere($qb->expr()->eq('st.circle', ':scope'));
|
||||
$qb->setParameter('scope', $params['scope']);
|
||||
}
|
||||
|
||||
if (array_key_exists('types', $params) && null !== $params['types']) {
|
||||
if (count($params['types']) > 0) {
|
||||
$qb->andWhere($qb->expr()->in('st.type', ':types'));
|
||||
$qb->setParameter('types', $params['types']);
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('date_status', $params) and !empty($params['date_status'])) {
|
||||
$this->addTypeFilter($qb, $params);
|
||||
}
|
||||
|
||||
if (array_key_exists('is_closed', $params)) {
|
||||
$qb->andWhere($this->buildIsClosed($qb, !$params['is_closed']));
|
||||
}
|
||||
}
|
||||
|
||||
private function buildIsClosed(QueryBuilder $qb, bool $negative = false)
|
||||
{
|
||||
if (false === $negative) {
|
||||
return $qb->expr()->eq('st.closed', "'TRUE'");
|
||||
}
|
||||
|
||||
return $qb->expr()->eq('st.closed', "'FALSE'");
|
||||
}
|
||||
|
||||
private function buildNowIsAfterEndDate(QueryBuilder $qb, $negative = false)
|
||||
{
|
||||
if (false === $negative) {
|
||||
return $qb->expr()->andX()
|
||||
->add($qb->expr()->isNotNull('st.endDate'))
|
||||
->add($qb->expr()->lte('st.endDate', ':now'));
|
||||
}
|
||||
|
||||
return $qb->expr()->orX()
|
||||
->add(
|
||||
$qb->expr()->andX()
|
||||
->add($qb->expr()->isNotNull('st.endDate'))
|
||||
->add($qb->expr()->gt('st.endDate', ':now'))
|
||||
)
|
||||
->add($qb->expr()->isNull('st.endDate'));
|
||||
}
|
||||
|
||||
private function buildNowIsAfterStartDate(QueryBuilder $qb, bool $negative = false)
|
||||
{
|
||||
if (false === $negative) {
|
||||
return $qb->expr()->orX()
|
||||
->add($qb->expr()->lte('st.startDate', ':now'))
|
||||
->add($qb->expr()->isNull('st.startDate'));
|
||||
}
|
||||
|
||||
return
|
||||
$qb->expr()->andX()
|
||||
->add($qb->expr()->gt('st.startDate', ':now'))
|
||||
->add($qb->expr()->isNotNull('st.startDate'));
|
||||
}
|
||||
|
||||
private function buildNowIsAfterWarningDate(QueryBuilder $qb, bool $negative = false)
|
||||
{
|
||||
if (false === $negative) {
|
||||
return $qb->expr()->andX()
|
||||
->add(
|
||||
$qb->expr()->lte(
|
||||
$qb->expr()->diff('st.endDate', 'st.warningInterval'),
|
||||
':now'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $qb->expr()->orX()
|
||||
->add(
|
||||
$qb->expr()->andX()
|
||||
->add($qb->expr()->isNotNull('st.endDate'))
|
||||
->add($qb->expr()->isNotNull('st.warningInterval'))
|
||||
->add(
|
||||
$qb->expr()->gt(
|
||||
$qb->expr()->diff('st.endDate', 'st.warningInterval'),
|
||||
':now'
|
||||
)
|
||||
)
|
||||
)
|
||||
->add($qb->expr()->isNull('st.endDate'))
|
||||
->add($qb->expr()->isNull('st.warningInterval'));
|
||||
}
|
||||
}
|
||||
|
@@ -1,47 +1,44 @@
|
||||
<?php
|
||||
/*
|
||||
*
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Security\Authorization;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class AuthorizationEvent extends Event
|
||||
{
|
||||
public const VOTE = 'chill_task.vote';
|
||||
|
||||
/**
|
||||
* @var Chill\TaskBundle\Entity\AbstractTask|\Chill\PersonBundle\Entity\Person|null
|
||||
*/
|
||||
protected $subject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $attribute;
|
||||
|
||||
|
||||
/**
|
||||
* @var \Chill\PersonBundle\Entity\Person|Chill\TaskBundle\Entity\AbstractTask|null
|
||||
*/
|
||||
protected $subject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TokenInterface
|
||||
*/
|
||||
protected $token;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $vote;
|
||||
|
||||
const VOTE = 'chill_task.vote';
|
||||
|
||||
|
||||
public function __construct(
|
||||
$subject,
|
||||
$attribute,
|
||||
$subject,
|
||||
$attribute,
|
||||
TokenInterface $token
|
||||
) {
|
||||
$this->subject = $subject;
|
||||
@@ -49,40 +46,40 @@ class AuthorizationEvent extends Event
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
public function getSubject()
|
||||
{
|
||||
return $this->subject;
|
||||
}
|
||||
|
||||
public function getAttribute()
|
||||
{
|
||||
return $this->attribute;
|
||||
}
|
||||
|
||||
public function getSubject()
|
||||
{
|
||||
return $this->subject;
|
||||
}
|
||||
|
||||
public function getToken(): TokenInterface
|
||||
{
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
|
||||
public function getVote()
|
||||
{
|
||||
return $this->vote;
|
||||
}
|
||||
|
||||
public function setVote($vote)
|
||||
{
|
||||
$this->vote = $vote;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasVote()
|
||||
{
|
||||
return $this->vote !== NULL;
|
||||
return null !== $this->vote;
|
||||
}
|
||||
|
||||
|
||||
public function removeVote()
|
||||
{
|
||||
$this->vote = NULL;
|
||||
$this->vote = null;
|
||||
}
|
||||
|
||||
public function setVote($vote)
|
||||
{
|
||||
$this->vote = $vote;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@@ -1,31 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\TaskBundle\Security\Authorization;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
|
||||
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
|
||||
use Chill\TaskBundle\Entity\AbstractTask;
|
||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
||||
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;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Chill\TaskBundle\Entity\AbstractTask;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
|
||||
use function in_array;
|
||||
|
||||
final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
|
||||
{
|
||||
public const CREATE_COURSE = 'CHILL_TASK_TASK_CREATE_FOR_COURSE';
|
||||
|
||||
public const CREATE_PERSON = 'CHILL_TASK_TASK_CREATE_FOR_PERSON';
|
||||
public const DELETE = 'CHILL_TASK_TASK_DELETE';
|
||||
public const SHOW = 'CHILL_TASK_TASK_SHOW';
|
||||
public const UPDATE = 'CHILL_TASK_TASK_UPDATE';
|
||||
|
||||
public const DELETE = 'CHILL_TASK_TASK_DELETE';
|
||||
|
||||
public const ROLES = [
|
||||
self::CREATE_COURSE,
|
||||
@@ -35,12 +43,16 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy
|
||||
self::UPDATE,
|
||||
];
|
||||
|
||||
public const SHOW = 'CHILL_TASK_TASK_SHOW';
|
||||
|
||||
public const UPDATE = 'CHILL_TASK_TASK_UPDATE';
|
||||
|
||||
private AccessDecisionManagerInterface $accessDecisionManager;
|
||||
|
||||
private LoggerInterface $logger;
|
||||
|
||||
private EventDispatcherInterface $eventDispatcher;
|
||||
|
||||
private LoggerInterface $logger;
|
||||
|
||||
private VoterHelperInterface $voter;
|
||||
|
||||
public function __construct(
|
||||
@@ -62,6 +74,23 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy
|
||||
->build();
|
||||
}
|
||||
|
||||
public function getRoles(): array
|
||||
{
|
||||
return self::ROLES;
|
||||
}
|
||||
|
||||
public function getRolesWithHierarchy(): array
|
||||
{
|
||||
return [
|
||||
'Task' => self::ROLES,
|
||||
];
|
||||
}
|
||||
|
||||
public function getRolesWithoutScope(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function supports($attribute, $subject)
|
||||
{
|
||||
return $this->voter->supports($attribute, $subject);
|
||||
@@ -69,7 +98,7 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy
|
||||
|
||||
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
|
||||
{
|
||||
$this->logger->debug(sprintf("Voting from %s class", self::class));
|
||||
$this->logger->debug(sprintf('Voting from %s class', self::class));
|
||||
|
||||
if (!$token->getUser() instanceof User) {
|
||||
return false;
|
||||
@@ -80,10 +109,10 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy
|
||||
$this->eventDispatcher->dispatch(AuthorizationEvent::VOTE, $event);
|
||||
|
||||
if ($event->hasVote()) {
|
||||
$this->logger->debug("The TaskVoter is overriding by "
|
||||
.AuthorizationEvent::VOTE, [
|
||||
$this->logger->debug('The TaskVoter is overriding by '
|
||||
. AuthorizationEvent::VOTE, [
|
||||
'vote' => $event->getVote(),
|
||||
'task_id' => $subject->getId()
|
||||
'task_id' => $subject->getId(),
|
||||
]);
|
||||
|
||||
return $event->getVote();
|
||||
@@ -97,11 +126,11 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NULL !== $person = $subject->getPerson()) {
|
||||
if (null !== $person = $subject->getPerson()) {
|
||||
if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
|
||||
return false;
|
||||
}
|
||||
} elseif (NULL !== $period = $subject->getCourse()) {
|
||||
} elseif (null !== $period = $subject->getCourse()) {
|
||||
if (!$this->accessDecisionManager->decide($token, [AccompanyingPeriodVoter::SEE], $period)) {
|
||||
return false;
|
||||
}
|
||||
@@ -110,7 +139,7 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy
|
||||
|
||||
if ($subject instanceof AccompanyingPeriod) {
|
||||
if (AccompanyingPeriod::STEP_CLOSED === $subject->getStep()) {
|
||||
if (\in_array($attribute, [self::UPDATE, self::CREATE_COURSE, self::DELETE], true)) {
|
||||
if (in_array($attribute, [self::UPDATE, self::CREATE_COURSE, self::DELETE], true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -119,21 +148,4 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy
|
||||
// do regular check.
|
||||
return $this->voter->voteOnAttribute($attribute, $subject, $token);
|
||||
}
|
||||
|
||||
public function getRoles(): array
|
||||
{
|
||||
return self::ROLES;
|
||||
}
|
||||
|
||||
public function getRolesWithHierarchy(): array
|
||||
{
|
||||
return [
|
||||
'Task' => self::ROLES
|
||||
];
|
||||
}
|
||||
|
||||
public function getRolesWithoutScope(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@@ -1,58 +1,43 @@
|
||||
<?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\Templating;
|
||||
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
use Chill\TaskBundle\Entity\AbstractTask;
|
||||
use Chill\TaskBundle\Workflow\TaskWorkflowManager;
|
||||
|
||||
/**
|
||||
*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Templating;
|
||||
|
||||
use Chill\TaskBundle\Entity\AbstractTask;
|
||||
use Chill\TaskBundle\Workflow\TaskWorkflowManager;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
class TaskTwigExtension extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var TaskWorkflowManager
|
||||
*/
|
||||
protected $taskWorkflowManager;
|
||||
|
||||
|
||||
public function __construct(TaskWorkflowManager $taskWorkflowManager)
|
||||
{
|
||||
$this->taskWorkflowManager = $taskWorkflowManager;
|
||||
}
|
||||
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction('task_workflow_metadata', [ $this, 'getWorkflowMetadata' ] )
|
||||
new TwigFunction('task_workflow_metadata', [$this, 'getWorkflowMetadata']),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function getWorkflowMetadata(
|
||||
AbstractTask $task,
|
||||
string $key,
|
||||
$metadataSubject = null,
|
||||
string $name = null
|
||||
AbstractTask $task,
|
||||
string $key,
|
||||
$metadataSubject = null,
|
||||
?string $name = null
|
||||
) {
|
||||
return $this->taskWorkflowManager->getWorkflowMetadata($task, $key, $metadataSubject, $name);
|
||||
}
|
||||
|
@@ -1,50 +1,36 @@
|
||||
<?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\Templating\UI;
|
||||
|
||||
use Chill\MainBundle\Templating\UI\NotificationCounterInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\TaskBundle\Repository\SingleTaskRepository;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Workflow\Event\Event;
|
||||
|
||||
/**
|
||||
*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Templating\UI;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Templating\UI\NotificationCounterInterface;
|
||||
use Chill\TaskBundle\Repository\SingleTaskRepository;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Workflow\Event\Event;
|
||||
use function array_merge;
|
||||
|
||||
class CountNotificationTask implements NotificationCounterInterface
|
||||
{
|
||||
public const CACHE_KEY = 'chill_task.count_notifications.user.%d.%s';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var SingleTaskRepository
|
||||
*/
|
||||
protected $singleTaskRepository;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var CacheItempPoolInterface
|
||||
*/
|
||||
protected $cachePool;
|
||||
|
||||
const CACHE_KEY = 'chill_task.count_notifications.user.%d.%s';
|
||||
|
||||
|
||||
/**
|
||||
* @var SingleTaskRepository
|
||||
*/
|
||||
protected $singleTaskRepository;
|
||||
|
||||
public function __construct(
|
||||
SingleTaskRepository $singleTaskRepository,
|
||||
CacheItemPoolInterface $cachePool
|
||||
@@ -52,65 +38,38 @@ class CountNotificationTask implements NotificationCounterInterface
|
||||
$this->singleTaskRepository = $singleTaskRepository;
|
||||
$this->cachePool = $cachePool;
|
||||
}
|
||||
|
||||
public function countNotification(UserInterface $u): int
|
||||
{
|
||||
return
|
||||
$this->countNotificationEnded($u)
|
||||
+ $this->countNotificationWarning($u);
|
||||
}
|
||||
|
||||
public function countNotificationEnded(UserInterface $u): int
|
||||
{
|
||||
return $this->_countNotification($u, SingleTaskRepository::DATE_STATUS_ENDED);
|
||||
}
|
||||
|
||||
public function countNotificationWarning(UserInterface $u): int
|
||||
{
|
||||
return $this->_countNotification($u, SingleTaskRepository::DATE_STATUS_WARNING);
|
||||
}
|
||||
|
||||
protected function _countNotification(UserInterface $u, $status)
|
||||
{
|
||||
if (!$u instanceof User) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$sumCache = $this->cachePool->getItem($this->getCacheKey($u, $status));
|
||||
|
||||
if ($sumCache->isHit()) {
|
||||
return $sumCache->get();
|
||||
}
|
||||
|
||||
$params = [
|
||||
'user' => $u,
|
||||
'is_closed' => false
|
||||
];
|
||||
|
||||
$sum = $this->singleTaskRepository->countByParameters(
|
||||
\array_merge($params, [ 'date_status' => $status ])
|
||||
);
|
||||
|
||||
$sumCache->set($sum);
|
||||
$this->cachePool->save($sumCache);
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
|
||||
public function addNotification(UserInterface $u): int
|
||||
{
|
||||
return $this->countNotification($u);
|
||||
}
|
||||
|
||||
|
||||
public function countNotification(UserInterface $u): int
|
||||
{
|
||||
return
|
||||
$this->countNotificationEnded($u)
|
||||
+ $this->countNotificationWarning($u);
|
||||
}
|
||||
|
||||
public function countNotificationEnded(UserInterface $u): int
|
||||
{
|
||||
return $this->_countNotification($u, SingleTaskRepository::DATE_STATUS_ENDED);
|
||||
}
|
||||
|
||||
public function countNotificationWarning(UserInterface $u): int
|
||||
{
|
||||
return $this->_countNotification($u, SingleTaskRepository::DATE_STATUS_WARNING);
|
||||
}
|
||||
|
||||
public function resetCacheOnNewStates(Event $e)
|
||||
{
|
||||
/* @var $task \Chill\TaskBundle\Entity\SingleTask */
|
||||
$task = $e->getSubject();
|
||||
|
||||
if (NULL !== $task->getAssignee()) {
|
||||
|
||||
if (null !== $task->getAssignee()) {
|
||||
foreach ([
|
||||
SingleTaskRepository::DATE_STATUS_ENDED,
|
||||
SingleTaskRepository::DATE_STATUS_WARNING
|
||||
SingleTaskRepository::DATE_STATUS_WARNING,
|
||||
] as $status) {
|
||||
$key = $this->getCacheKey($task->getAssignee(), $status);
|
||||
$sumCache = $this->cachePool->getItem($key);
|
||||
@@ -121,9 +80,36 @@ class CountNotificationTask implements NotificationCounterInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function _countNotification(UserInterface $u, $status)
|
||||
{
|
||||
if (!$u instanceof User) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$sumCache = $this->cachePool->getItem($this->getCacheKey($u, $status));
|
||||
|
||||
if ($sumCache->isHit()) {
|
||||
return $sumCache->get();
|
||||
}
|
||||
|
||||
$params = [
|
||||
'user' => $u,
|
||||
'is_closed' => false,
|
||||
];
|
||||
|
||||
$sum = $this->singleTaskRepository->countByParameters(
|
||||
array_merge($params, ['date_status' => $status])
|
||||
);
|
||||
|
||||
$sumCache->set($sum);
|
||||
$this->cachePool->save($sumCache);
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
private function getCacheKey(User $u, $status)
|
||||
{
|
||||
return sprintf(self::CACHE_KEY, $u->getId(), $status);
|
||||
return sprintf(self::CACHE_KEY, $u->getId(), $status);
|
||||
}
|
||||
}
|
||||
|
@@ -1,102 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Tests\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Tests\TestHelper;
|
||||
use Faker;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use function array_filter;
|
||||
use function array_rand;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class SingleTaskControllerTest extends WebTestCase
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var Faker\Generator
|
||||
*/
|
||||
protected $faker;
|
||||
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->faker = Faker\Factory::create('fr');
|
||||
}
|
||||
|
||||
|
||||
public function testNew()
|
||||
{
|
||||
$client = static::createClient(
|
||||
[],
|
||||
TestHelper::getAuthenticatedClientOptions()
|
||||
);
|
||||
$person = $this->getRandomPerson('Center A');
|
||||
|
||||
$crawler = $client->request('GET', '/fr/task/single-task/new', [
|
||||
'person_id' => $person->getId(),
|
||||
]);
|
||||
var_dump($crawler->text());
|
||||
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$form = $crawler->selectButton('Envoi')->form();
|
||||
|
||||
$title = $this->faker->sentence;
|
||||
$circles = $form->get('circle')
|
||||
->availableOptionsValues();
|
||||
|
||||
$client->submit($form, [
|
||||
'title' => $title,
|
||||
'circle' => $circles[array_rand($circles)],
|
||||
]);
|
||||
|
||||
$this->assertTrue($client->getResponse()->isRedirect(sprintf(
|
||||
'/fr/task/task/list/%d',
|
||||
$person->getId()
|
||||
)));
|
||||
|
||||
$crawler = $client->followRedirect();
|
||||
|
||||
$this->assertContains(
|
||||
$title,
|
||||
$crawler->text(),
|
||||
'Assert that newly created task title is shown in list page'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mixed $centerName
|
||||
*
|
||||
* @return \Chill\PersonBundle\Entity\Person
|
||||
*/
|
||||
protected function getRandomPerson($centerName)
|
||||
{
|
||||
$em = self::$kernel
|
||||
->getContainer()
|
||||
->get('doctrine.orm.entity_manager')
|
||||
;
|
||||
|
||||
->get('doctrine.orm.entity_manager');
|
||||
|
||||
$centers = $em
|
||||
->getRepository(Center::class)
|
||||
->findAll();
|
||||
|
||||
$center = \array_filter(
|
||||
$centers,
|
||||
function(Center $c) use ($centerName) {
|
||||
return $centerName === $c->getName();
|
||||
})[0];
|
||||
|
||||
|
||||
$center = array_filter(
|
||||
$centers,
|
||||
function (Center $c) use ($centerName) {
|
||||
return $c->getName() === $centerName;
|
||||
}
|
||||
)[0];
|
||||
|
||||
$ids = $em
|
||||
->createQuery('SELECT p.id FROM ChillPersonBundle:Person p '
|
||||
->createQuery(
|
||||
'SELECT p.id FROM ChillPersonBundle:Person p '
|
||||
. 'WHERE p.center = :center'
|
||||
)
|
||||
)
|
||||
->setParameter('center', $center)
|
||||
->getResult()
|
||||
;
|
||||
|
||||
$id = $ids[\array_rand($ids)];
|
||||
|
||||
->getResult();
|
||||
|
||||
$id = $ids[array_rand($ids)];
|
||||
|
||||
return self::$kernel
|
||||
->getContainer()
|
||||
->get('doctrine.orm.entity_manager')
|
||||
->getRepository(\Chill\PersonBundle\Entity\Person::class)
|
||||
->find($id)
|
||||
;
|
||||
->find($id);
|
||||
}
|
||||
|
||||
public function testNew()
|
||||
{
|
||||
$client = static::createClient(
|
||||
array(),
|
||||
TestHelper::getAuthenticatedClientOptions()
|
||||
);
|
||||
$person = $this->getRandomPerson('Center A');
|
||||
|
||||
$crawler = $client->request('GET', '/fr/task/single-task/new', [
|
||||
'person_id' => $person->getId()
|
||||
]);
|
||||
var_dump($crawler->text());
|
||||
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
|
||||
|
||||
$form = $crawler->selectButton('Envoi')->form();
|
||||
|
||||
$title = $this->faker->sentence;
|
||||
$circles = $form->get('circle')
|
||||
->availableOptionsValues()
|
||||
;
|
||||
|
||||
$client->submit($form, [
|
||||
'title' => $title,
|
||||
'circle' => $circles[\array_rand($circles)]
|
||||
]);
|
||||
|
||||
$this->assertTrue($client->getResponse()->isRedirect(sprintf(
|
||||
'/fr/task/task/list/%d', $person->getId())));
|
||||
|
||||
$crawler = $client->followRedirect();
|
||||
|
||||
$this->assertContains($title, $crawler->text(),
|
||||
"Assert that newly created task title is shown in list page")
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,9 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Tests\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class TaskControllerTest extends WebTestCase
|
||||
{
|
||||
}
|
||||
|
@@ -1,50 +1,42 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* 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/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Timeline;
|
||||
|
||||
use Chill\MainBundle\Timeline\TimelineProviderInterface;
|
||||
use Chill\MainBundle\Timeline\TimelineSingleQuery;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Chill\TaskBundle\Entity\Task\SingleTaskPlaceEvent;
|
||||
use Chill\TaskBundle\Entity\SingleTask;
|
||||
use Chill\TaskBundle\Entity\Task\SingleTaskPlaceEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use LogicException;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
use Symfony\Component\Workflow\Workflow;
|
||||
use function array_combine;
|
||||
use function array_map;
|
||||
|
||||
/**
|
||||
* Provide timeline elements related to tasks, in tasks context
|
||||
*
|
||||
* Provide timeline elements related to tasks, in tasks context.
|
||||
*/
|
||||
class SingleTaskTaskLifeCycleEventTimelineProvider implements TimelineProviderInterface
|
||||
{
|
||||
public const TYPE = 'chill_task.transition';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var EntityManagerInterface
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Registry
|
||||
*/
|
||||
protected $registry;
|
||||
|
||||
const TYPE = 'chill_task.transition';
|
||||
|
||||
public function __construct(EntityManagerInterface $em, Registry $registry)
|
||||
{
|
||||
$this->em = $em;
|
||||
@@ -53,8 +45,8 @@ class SingleTaskTaskLifeCycleEventTimelineProvider implements TimelineProviderIn
|
||||
|
||||
public function fetchQuery($context, $args)
|
||||
{
|
||||
if ($context !== 'task') {
|
||||
throw new \LogicException(sprintf('%s is not able '
|
||||
if ('task' !== $context) {
|
||||
throw new LogicException(sprintf('%s is not able '
|
||||
. 'to render context %s', self::class, $context));
|
||||
}
|
||||
|
||||
@@ -67,33 +59,33 @@ class SingleTaskTaskLifeCycleEventTimelineProvider implements TimelineProviderIn
|
||||
'id' => sprintf('%s.%s.%s', $metadata->getSchemaName(), $metadata->getTableName(), $metadata->getColumnName('id')),
|
||||
'type' => self::TYPE,
|
||||
'date' => $metadata->getColumnName('datetime'),
|
||||
'FROM' => sprintf('%s JOIN %s ON %s = %s',
|
||||
'FROM' => sprintf(
|
||||
'%s JOIN %s ON %s = %s',
|
||||
sprintf('%s.%s', $metadata->getSchemaName(), $metadata->getTableName()),
|
||||
sprintf('%s.%s', $singleTaskMetadata->getSchemaName(), $singleTaskMetadata->getTableName()),
|
||||
$metadata->getAssociationMapping('task')['joinColumns'][0]['name'],
|
||||
sprintf('%s.%s.%s', $singleTaskMetadata->getSchemaName(), $singleTaskMetadata->getTableName(), $singleTaskMetadata->getColumnName('id'))
|
||||
),
|
||||
'WHERE' => sprintf('%s.%s = %d',
|
||||
),
|
||||
'WHERE' => sprintf(
|
||||
'%s.%s = %d',
|
||||
sprintf('%s.%s', $singleTaskMetadata->getSchemaName(), $singleTaskMetadata->getTableName()),
|
||||
$singleTaskMetadata->getColumnName('id'),
|
||||
$args['task']->getId()
|
||||
),
|
||||
),
|
||||
'parameters' => [],
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
public function getEntities(array $ids)
|
||||
{
|
||||
$events = $this->em
|
||||
->getRepository(SingleTaskPlaceEvent::class)
|
||||
->findBy([ 'id' => $ids ])
|
||||
;
|
||||
->findBy(['id' => $ids]);
|
||||
|
||||
return \array_combine(
|
||||
\array_map(function($e) { return $e->getId(); }, $events ),
|
||||
return array_combine(
|
||||
array_map(function ($e) { return $e->getId(); }, $events),
|
||||
$events
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
public function getEntityTemplate($entity, $context, array $args)
|
||||
@@ -102,21 +94,25 @@ class SingleTaskTaskLifeCycleEventTimelineProvider implements TimelineProviderIn
|
||||
$workflow = $this->registry->get($entity->getTask(), $entity->getData()['workflow']);
|
||||
$transition = $this->getTransitionByName($entity->getTransition(), $workflow);
|
||||
}
|
||||
|
||||
|
||||
return [
|
||||
'template' => 'ChillTaskBundle:Timeline:single_task_transition_task_context.html.twig',
|
||||
'template_data' => [
|
||||
'task' => $args['task'],
|
||||
'event' => $entity,
|
||||
'transition' => $transition ?? null
|
||||
]
|
||||
'transition' => $transition ?? null,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function supportsType($type): bool
|
||||
{
|
||||
return self::TYPE === $type;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $name
|
||||
* @param Workflow $workflow
|
||||
*
|
||||
* @return \Symfony\Component\Workflow\Transition
|
||||
*/
|
||||
protected function getTransitionByName($name, Workflow $workflow)
|
||||
@@ -127,9 +123,4 @@ class SingleTaskTaskLifeCycleEventTimelineProvider implements TimelineProviderIn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function supportsType($type): bool
|
||||
{
|
||||
return $type === self::TYPE;
|
||||
}
|
||||
}
|
||||
|
@@ -1,53 +1,49 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* 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/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Timeline;
|
||||
|
||||
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Chill\MainBundle\Timeline\TimelineProviderInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Chill\TaskBundle\Entity\Task\SingleTaskPlaceEvent;
|
||||
use Chill\TaskBundle\Entity\SingleTask;
|
||||
use Chill\MainBundle\Timeline\TimelineSingleQuery;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\TaskBundle\Entity\SingleTask;
|
||||
use Chill\TaskBundle\Entity\Task\SingleTaskPlaceEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
use Symfony\Component\Workflow\Workflow;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Chill\MainBundle\Timeline\TimelineSingleQuery;
|
||||
use UnexpectedValueException;
|
||||
use function array_combine;
|
||||
use function array_fill;
|
||||
use function array_map;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function strtr;
|
||||
|
||||
/**
|
||||
* Provide element for timeline for 'person' and 'center' context
|
||||
*
|
||||
* Provide element for timeline for 'person' and 'center' context.
|
||||
*/
|
||||
class TaskLifeCycleEventTimelineProvider implements TimelineProviderInterface
|
||||
{
|
||||
public const TYPE = 'chill_task.transition';
|
||||
|
||||
protected AuthorizationHelper $authorizationHelper;
|
||||
|
||||
protected EntityManagerInterface $em;
|
||||
|
||||
protected Registry $registry;
|
||||
|
||||
protected AuthorizationHelper $authorizationHelper;
|
||||
|
||||
protected Security $security;
|
||||
|
||||
|
||||
const TYPE = 'chill_task.transition';
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $em,
|
||||
Registry $registry,
|
||||
@@ -68,12 +64,16 @@ class TaskLifeCycleEventTimelineProvider implements TimelineProviderInterface
|
||||
switch ($context) {
|
||||
case 'person':
|
||||
[ $where, $parameters ] = $this->getWhereClauseForPerson($args['person']);
|
||||
|
||||
break;
|
||||
|
||||
case 'center':
|
||||
[ $where, $parameters ] = $this->getWhereClauseForCenter($args['centers']);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \UnexpectedValueException("context {$context} is not supported");
|
||||
throw new UnexpectedValueException("context {$context} is not supported");
|
||||
}
|
||||
|
||||
return TimelineSingleQuery::fromArray([
|
||||
@@ -82,160 +82,26 @@ class TaskLifeCycleEventTimelineProvider implements TimelineProviderInterface
|
||||
'date' => $metadata->getColumnName('datetime'),
|
||||
'FROM' => $this->getFromClause($context),
|
||||
'WHERE' => $where,
|
||||
'parameters' => $parameters
|
||||
'parameters' => $parameters,
|
||||
]);
|
||||
}
|
||||
|
||||
private function getWhereClauseForCenter(array $centers): array
|
||||
{
|
||||
$taskEvent = $this->em->getClassMetadata(SingleTaskPlaceEvent::class);
|
||||
$singleTask = $this->em->getClassMetadata(SingleTask::class);
|
||||
$person = $this->em->getClassMetadata(Person::class);
|
||||
$personFkCenter = $person->getAssociationMapping('center')['joinColumns'][0]['name'];
|
||||
$taskFkCircle = $singleTask->getAssociationMapping('circle')['joinColumns'][0]['name'];
|
||||
|
||||
// the parameters
|
||||
$parameters = [];
|
||||
|
||||
// the clause that we will repeat for each center, joined by 'OR'
|
||||
$clause = "{person}.{center_id} = ? AND {task}.{circle} IN ({circle_ids})";
|
||||
|
||||
// array to gather clauses
|
||||
$clauses = [];
|
||||
|
||||
// loop over centers
|
||||
foreach ($this->authorizationHelper->getReachableCenters(
|
||||
$this->security->getUser(), new Role(ActivityVoter::SEE_DETAILS)) as $center) {
|
||||
|
||||
if (FALSE === \in_array($center, $centers)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// fill center parameter
|
||||
$parameters[] = $center->getId();
|
||||
|
||||
// we loop over circles
|
||||
$circles = $this->authorizationHelper->getReachableCircles(
|
||||
$this->security->getUser(), new Role(ActivityVoter::SEE_DETAILS), $center);
|
||||
$circleIds = [];
|
||||
|
||||
foreach ($circles as $circle) {
|
||||
$parameters[] = $circleIds[] = $circle->getId();
|
||||
}
|
||||
|
||||
$clauses[] = \strtr(
|
||||
$clause,
|
||||
[
|
||||
'{person}' => $person->getTableName(),
|
||||
'{center_id}' => $personFkCenter,
|
||||
'{task}' => $singleTask->getSchemaName().".".$singleTask->getTableName(),
|
||||
'{circle}' => $taskFkCircle,
|
||||
'{circle_ids}' => \implode(', ', \array_fill(0, count($circleIds), '?'))
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (0 === \count($clauses)) {
|
||||
return [ 'FALSE = TRUE' , [] ];
|
||||
}
|
||||
|
||||
return [
|
||||
\implode(' OR ', $clauses),
|
||||
$parameters
|
||||
];
|
||||
}
|
||||
|
||||
private function getWhereClauseForPerson(Person $personArg): array
|
||||
{
|
||||
$taskEvent = $this->em->getClassMetadata(SingleTaskPlaceEvent::class);
|
||||
$singleTask = $this->em->getClassMetadata(SingleTask::class);
|
||||
$person = $this->em->getClassMetadata(Person::class);
|
||||
$eventFkTask = $taskEvent->getAssociationMapping('task')['joinColumns'][0]['name'];
|
||||
$taskFkPerson = $singleTask->getAssociationMapping('person')['joinColumns'][0]['name'];
|
||||
$personPk = $singleTask->getAssociationMapping('person')['joinColumns'][0]['referencedColumnName'];
|
||||
$taskFkCircle = $singleTask->getAssociationMapping('circle')['joinColumns'][0]['name'];
|
||||
|
||||
|
||||
// the parameters
|
||||
$parameters = $circleIds = [];
|
||||
|
||||
// the clause that we will fill
|
||||
$clause = "{person}.{person_id} = ? AND {task}.{circle} IN ({circle_ids})";
|
||||
|
||||
// person is the first parameter
|
||||
$parameters[] = $personArg->getId();
|
||||
|
||||
// we loop over circles
|
||||
$circles = $this->authorizationHelper->getReachableCircles(
|
||||
$this->security->getUser(), new Role(ActivityVoter::SEE_DETAILS), $personArg->getCenter());
|
||||
|
||||
if (0 === count($circles)) {
|
||||
// go fast to block access to every tasks
|
||||
return [ "FALSE = TRUE", [] ];
|
||||
}
|
||||
|
||||
foreach ($circles as $circle) {
|
||||
$parameters[] = $circleIds[] = $circle->getId();
|
||||
}
|
||||
|
||||
return [
|
||||
\strtr(
|
||||
$clause,
|
||||
[
|
||||
'{person}' => $person->getTableName(),
|
||||
'{person_id}' => $person->getColumnName('id'),
|
||||
'{task}' => $singleTask->getSchemaName().".".$singleTask->getTableName(),
|
||||
'{circle}' => $taskFkCircle,
|
||||
'{circle_ids}' => \implode(', ', \array_fill(0, count($circleIds), '?'))
|
||||
]
|
||||
),
|
||||
$parameters
|
||||
];
|
||||
}
|
||||
|
||||
private function getFromClause(string $context)
|
||||
{
|
||||
$taskEvent = $this->em->getClassMetadata(SingleTaskPlaceEvent::class);
|
||||
$singleTask = $this->em->getClassMetadata(SingleTask::class);
|
||||
$person = $this->em->getClassMetadata(Person::class);
|
||||
$eventFkTask = $taskEvent->getAssociationMapping('task')['joinColumns'][0]['name'];
|
||||
$taskFkPerson = $singleTask->getAssociationMapping('person')['joinColumns'][0]['name'];
|
||||
$personPk = $singleTask->getAssociationMapping('person')['joinColumns'][0]['referencedColumnName'];
|
||||
|
||||
$from = "{single_task_event} ".
|
||||
"JOIN {single_task} ON {single_task}.{task_pk} = {single_task_event}.{event_fk_task} ".
|
||||
"JOIN {person} ON {single_task}.{task_person_fk} = {person}.{person_pk}";
|
||||
|
||||
return \strtr(
|
||||
$from,
|
||||
[
|
||||
'{single_task}' => sprintf('%s.%s', $singleTask->getSchemaName(), $singleTask->getTableName()),
|
||||
'{single_task_event}' => sprintf('%s.%s', $taskEvent->getSchemaName(), $taskEvent->getTableName()),
|
||||
'{task_pk}' => $singleTask->getColumnName('id'),
|
||||
'{event_fk_task}' => $eventFkTask,
|
||||
'{person}' => $person->getTableName(),
|
||||
'{task_person_fk}' => $taskFkPerson,
|
||||
'{person_pk}' => $personPk
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getEntities(array $ids)
|
||||
{
|
||||
$events = $this->em
|
||||
->getRepository(SingleTaskPlaceEvent::class)
|
||||
->findBy([ 'id' => $ids ])
|
||||
;
|
||||
->findBy(['id' => $ids]);
|
||||
|
||||
return \array_combine(
|
||||
\array_map(function($e) { return $e->getId(); }, $events ),
|
||||
return array_combine(
|
||||
array_map(function ($e) { return $e->getId(); }, $events),
|
||||
$events
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
public function getEntityTemplate($entity, $context, array $args)
|
||||
{
|
||||
$workflow = $this->registry->get($entity->getTask(),
|
||||
$workflow = $this->registry->get(
|
||||
$entity->getTask(),
|
||||
(isset($entity->getData()['workflow'])) ? $entity->getData()['workflow'] : null
|
||||
);
|
||||
// sf4 check: prevent error message:
|
||||
@@ -251,15 +117,19 @@ class TaskLifeCycleEventTimelineProvider implements TimelineProviderInterface
|
||||
'context' => $context,
|
||||
'event' => $entity,
|
||||
'task' => $entity->getTask(),
|
||||
'transition' => $transition
|
||||
]
|
||||
'transition' => $transition,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function supportsType($type): bool
|
||||
{
|
||||
return self::TYPE === $type;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $name
|
||||
* @param Workflow $workflow
|
||||
*
|
||||
* @return \Symfony\Component\Workflow\Transition
|
||||
*/
|
||||
protected function getTransitionByName($name, Workflow $workflow)
|
||||
@@ -271,8 +141,143 @@ class TaskLifeCycleEventTimelineProvider implements TimelineProviderInterface
|
||||
}
|
||||
}
|
||||
|
||||
public function supportsType($type): bool
|
||||
private function getFromClause(string $context)
|
||||
{
|
||||
return $type === self::TYPE;
|
||||
$taskEvent = $this->em->getClassMetadata(SingleTaskPlaceEvent::class);
|
||||
$singleTask = $this->em->getClassMetadata(SingleTask::class);
|
||||
$person = $this->em->getClassMetadata(Person::class);
|
||||
$eventFkTask = $taskEvent->getAssociationMapping('task')['joinColumns'][0]['name'];
|
||||
$taskFkPerson = $singleTask->getAssociationMapping('person')['joinColumns'][0]['name'];
|
||||
$personPk = $singleTask->getAssociationMapping('person')['joinColumns'][0]['referencedColumnName'];
|
||||
|
||||
$from = '{single_task_event} ' .
|
||||
'JOIN {single_task} ON {single_task}.{task_pk} = {single_task_event}.{event_fk_task} ' .
|
||||
'JOIN {person} ON {single_task}.{task_person_fk} = {person}.{person_pk}';
|
||||
|
||||
return strtr(
|
||||
$from,
|
||||
[
|
||||
'{single_task}' => sprintf('%s.%s', $singleTask->getSchemaName(), $singleTask->getTableName()),
|
||||
'{single_task_event}' => sprintf('%s.%s', $taskEvent->getSchemaName(), $taskEvent->getTableName()),
|
||||
'{task_pk}' => $singleTask->getColumnName('id'),
|
||||
'{event_fk_task}' => $eventFkTask,
|
||||
'{person}' => $person->getTableName(),
|
||||
'{task_person_fk}' => $taskFkPerson,
|
||||
'{person_pk}' => $personPk,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function getWhereClauseForCenter(array $centers): array
|
||||
{
|
||||
$taskEvent = $this->em->getClassMetadata(SingleTaskPlaceEvent::class);
|
||||
$singleTask = $this->em->getClassMetadata(SingleTask::class);
|
||||
$person = $this->em->getClassMetadata(Person::class);
|
||||
$personFkCenter = $person->getAssociationMapping('center')['joinColumns'][0]['name'];
|
||||
$taskFkCircle = $singleTask->getAssociationMapping('circle')['joinColumns'][0]['name'];
|
||||
|
||||
// the parameters
|
||||
$parameters = [];
|
||||
|
||||
// the clause that we will repeat for each center, joined by 'OR'
|
||||
$clause = '{person}.{center_id} = ? AND {task}.{circle} IN ({circle_ids})';
|
||||
|
||||
// array to gather clauses
|
||||
$clauses = [];
|
||||
|
||||
// loop over centers
|
||||
foreach ($this->authorizationHelper->getReachableCenters(
|
||||
$this->security->getUser(),
|
||||
new Role(ActivityVoter::SEE_DETAILS)
|
||||
) as $center) {
|
||||
if (false === in_array($center, $centers)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// fill center parameter
|
||||
$parameters[] = $center->getId();
|
||||
|
||||
// we loop over circles
|
||||
$circles = $this->authorizationHelper->getReachableCircles(
|
||||
$this->security->getUser(),
|
||||
new Role(ActivityVoter::SEE_DETAILS),
|
||||
$center
|
||||
);
|
||||
$circleIds = [];
|
||||
|
||||
foreach ($circles as $circle) {
|
||||
$parameters[] = $circleIds[] = $circle->getId();
|
||||
}
|
||||
|
||||
$clauses[] = strtr(
|
||||
$clause,
|
||||
[
|
||||
'{person}' => $person->getTableName(),
|
||||
'{center_id}' => $personFkCenter,
|
||||
'{task}' => $singleTask->getSchemaName() . '.' . $singleTask->getTableName(),
|
||||
'{circle}' => $taskFkCircle,
|
||||
'{circle_ids}' => implode(', ', array_fill(0, count($circleIds), '?')),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (0 === \count($clauses)) {
|
||||
return ['FALSE = TRUE', []];
|
||||
}
|
||||
|
||||
return [
|
||||
implode(' OR ', $clauses),
|
||||
$parameters,
|
||||
];
|
||||
}
|
||||
|
||||
private function getWhereClauseForPerson(Person $personArg): array
|
||||
{
|
||||
$taskEvent = $this->em->getClassMetadata(SingleTaskPlaceEvent::class);
|
||||
$singleTask = $this->em->getClassMetadata(SingleTask::class);
|
||||
$person = $this->em->getClassMetadata(Person::class);
|
||||
$eventFkTask = $taskEvent->getAssociationMapping('task')['joinColumns'][0]['name'];
|
||||
$taskFkPerson = $singleTask->getAssociationMapping('person')['joinColumns'][0]['name'];
|
||||
$personPk = $singleTask->getAssociationMapping('person')['joinColumns'][0]['referencedColumnName'];
|
||||
$taskFkCircle = $singleTask->getAssociationMapping('circle')['joinColumns'][0]['name'];
|
||||
|
||||
// the parameters
|
||||
$parameters = $circleIds = [];
|
||||
|
||||
// the clause that we will fill
|
||||
$clause = '{person}.{person_id} = ? AND {task}.{circle} IN ({circle_ids})';
|
||||
|
||||
// person is the first parameter
|
||||
$parameters[] = $personArg->getId();
|
||||
|
||||
// we loop over circles
|
||||
$circles = $this->authorizationHelper->getReachableCircles(
|
||||
$this->security->getUser(),
|
||||
new Role(ActivityVoter::SEE_DETAILS),
|
||||
$personArg->getCenter()
|
||||
);
|
||||
|
||||
if (0 === count($circles)) {
|
||||
// go fast to block access to every tasks
|
||||
return ['FALSE = TRUE', []];
|
||||
}
|
||||
|
||||
foreach ($circles as $circle) {
|
||||
$parameters[] = $circleIds[] = $circle->getId();
|
||||
}
|
||||
|
||||
return [
|
||||
strtr(
|
||||
$clause,
|
||||
[
|
||||
'{person}' => $person->getTableName(),
|
||||
'{person_id}' => $person->getColumnName('id'),
|
||||
'{task}' => $singleTask->getSchemaName() . '.' . $singleTask->getTableName(),
|
||||
'{circle}' => $taskFkCircle,
|
||||
'{circle_ids}' => implode(', ', array_fill(0, count($circleIds), '?')),
|
||||
]
|
||||
),
|
||||
$parameters,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -1,67 +1,52 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* 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/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Workflow\Definition;
|
||||
|
||||
use Chill\TaskBundle\Entity\AbstractTask;
|
||||
use Chill\TaskBundle\Entity\SingleTask;
|
||||
use LogicException;
|
||||
use Symfony\Component\Workflow\Transition;
|
||||
use function array_key_exists;
|
||||
use function array_slice;
|
||||
use function explode;
|
||||
use function implode;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class DefaultTaskDefinition implements \Chill\TaskBundle\Workflow\TaskWorkflowDefinition
|
||||
{
|
||||
const TRANSITION_METADATA = [
|
||||
public const DEFINITION_METADATA = [
|
||||
'name' => 'Default task',
|
||||
];
|
||||
|
||||
public const TRANSITION_METADATA = [
|
||||
'close' => [
|
||||
'verb' => 'close',
|
||||
'class' => 'btn btn-task-label btn-task-close',
|
||||
'sentence' => '%user% has closed the task',
|
||||
'sentence_confirmation' => 'Are you sure you want to close this task ?',
|
||||
'apply_transition_submit_label' => 'Close_verb'
|
||||
'apply_transition_submit_label' => 'Close_verb',
|
||||
],
|
||||
'cancel' => [
|
||||
'verb' => 'cancel',
|
||||
'class' => 'btn btn-task-label btn-task-cancel',
|
||||
'sentence' => '%user% has canceled the task',
|
||||
'sentence_confirmation' => 'Are you sure you want to cancel this task ?',
|
||||
'apply_transition_submit_label' => 'Set this task to cancel state'
|
||||
'apply_transition_submit_label' => 'Set this task to cancel state',
|
||||
],
|
||||
'start' => [
|
||||
'verb' => 'start',
|
||||
'class' => 'btn btn-task-label btn-task-start',
|
||||
'sentence' => '%user% has started the task',
|
||||
'sentence_confirmation' => 'Are you sure you want to start this task ?',
|
||||
'apply_transition_submit_label' => 'Start_verb'
|
||||
]
|
||||
'apply_transition_submit_label' => 'Start_verb',
|
||||
],
|
||||
];
|
||||
|
||||
const DEFINITION_METADATA = [
|
||||
'name' => 'Default task'
|
||||
];
|
||||
|
||||
public function supports(AbstractTask $task)
|
||||
{
|
||||
|
||||
return $task instanceof SingleTask
|
||||
&& $task->getType() === 'task_default';
|
||||
}
|
||||
|
||||
public static function getAssociatedWorkflowName()
|
||||
{
|
||||
@@ -73,38 +58,46 @@ class DefaultTaskDefinition implements \Chill\TaskBundle\Workflow\TaskWorkflowDe
|
||||
string $key,
|
||||
$metadataSubject = null
|
||||
) {
|
||||
$keys = \explode('.', $key);
|
||||
$keys = explode('.', $key);
|
||||
|
||||
switch($keys[0]) {
|
||||
switch ($keys[0]) {
|
||||
case 'transition':
|
||||
if (!$metadataSubject instanceof Transition) {
|
||||
throw new \LogicException("You must give a transition as metadatasubject");
|
||||
throw new LogicException('You must give a transition as metadatasubject');
|
||||
}
|
||||
|
||||
return $this->getTransitionMetadata(\implode('.', \array_slice($keys, 1)), $metadataSubject);
|
||||
return $this->getTransitionMetadata(implode('.', array_slice($keys, 1)), $metadataSubject);
|
||||
|
||||
case 'definition':
|
||||
return self::DEFINITION_METADATA[$keys[1]] ?? $key;
|
||||
|
||||
default:
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
public function isClosed(AbstractTask $task)
|
||||
{
|
||||
return array_key_exists('closed', $task->getCurrentStates())
|
||||
|| array_key_exists('canceled', $task->getCurrentStates());
|
||||
}
|
||||
|
||||
public function supports(AbstractTask $task)
|
||||
{
|
||||
return $task instanceof SingleTask
|
||||
&& $task->getType() === 'task_default';
|
||||
}
|
||||
|
||||
protected function getTransitionMetadata($key, Transition $transition)
|
||||
{
|
||||
if (!\array_key_exists($transition->getName(), self::TRANSITION_METADATA)) {
|
||||
if (!array_key_exists($transition->getName(), self::TRANSITION_METADATA)) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
if (!\array_key_exists($key, self::TRANSITION_METADATA[$transition->getName()])) {
|
||||
if (!array_key_exists($key, self::TRANSITION_METADATA[$transition->getName()])) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
return self::TRANSITION_METADATA[$transition->getName()][$key];
|
||||
}
|
||||
|
||||
public function isClosed(AbstractTask $task)
|
||||
{
|
||||
return \array_key_exists('closed', $task->getCurrentStates())
|
||||
|| \array_key_exists('canceled', $task->getCurrentStates());
|
||||
}
|
||||
}
|
||||
|
@@ -1,59 +1,47 @@
|
||||
<?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\Workflow\Event;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Workflow\Event\GuardEvent;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
|
||||
/**
|
||||
*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Workflow\Event;
|
||||
|
||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Workflow\Event\GuardEvent;
|
||||
|
||||
class DefaultTaskGuardEvent implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
'workflow.task_default.guard' => [
|
||||
'checkACL'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @var AuthorizationCheckerInterface
|
||||
*/
|
||||
protected $authorizationChecker;
|
||||
|
||||
|
||||
public function __construct(AuthorizationCheckerInterface $authorizationChecker)
|
||||
{
|
||||
$this->authorizationChecker = $authorizationChecker;
|
||||
}
|
||||
|
||||
|
||||
public function checkACL(GuardEvent $event)
|
||||
{
|
||||
if (FALSE === $this->authorizationChecker->isGranted(TaskVoter::UPDATE,
|
||||
$event->getSubject())) {
|
||||
if (false === $this->authorizationChecker->isGranted(
|
||||
TaskVoter::UPDATE,
|
||||
$event->getSubject()
|
||||
)) {
|
||||
$event->setBlocked(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
'workflow.task_default.guard' => [
|
||||
'checkACL',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -1,27 +1,14 @@
|
||||
<?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\Workflow;
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Workflow;
|
||||
|
||||
interface TaskWorkflowDefinition
|
||||
{
|
||||
|
||||
}
|
||||
|
@@ -1,93 +1,84 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* 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/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TaskBundle\Workflow;
|
||||
|
||||
use Chill\TaskBundle\Entity\AbstractTask;
|
||||
use LogicException;
|
||||
use Symfony\Component\Workflow\Event\Event;
|
||||
use Symfony\Component\Workflow\SupportStrategy\WorkflowSupportStrategyInterface;
|
||||
use Symfony\Component\Workflow\WorkflowInterface;
|
||||
use Symfony\Component\Workflow\Event\Event;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
class TaskWorkflowManager implements WorkflowSupportStrategyInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var TaskWorkflowDefinition[]
|
||||
*/
|
||||
protected $definitions = array();
|
||||
|
||||
public function addDefinition(TaskWorkflowDefinition $definition) {
|
||||
protected $definitions = [];
|
||||
|
||||
public function addDefinition(TaskWorkflowDefinition $definition)
|
||||
{
|
||||
$this->definitions[] = $definition;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param AbstractTask $task
|
||||
* @throws LogicException
|
||||
*
|
||||
* @return TaskWorkflowDefinition
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function getTaskWorkflowDefinition(AbstractTask $task)
|
||||
{
|
||||
$definitions = array();
|
||||
|
||||
foreach($this->definitions as $tested) {
|
||||
$definitions = [];
|
||||
|
||||
foreach ($this->definitions as $tested) {
|
||||
if ($tested->supports($task)) {
|
||||
$definitions[] = $tested;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$count = count($definitions);
|
||||
if ($count > 1) {
|
||||
throw new \LogicException("More than one TaskWorkflowDefinition supports "
|
||||
. "this task. This should not happens.");
|
||||
} elseif ($count === 0) {
|
||||
throw new \LogicException(\sprintf("No taskWorkflowDefinition supports this task type: %s (task id: %s).", $task->getType(), $task->getId()));
|
||||
|
||||
if (1 < $count) {
|
||||
throw new LogicException('More than one TaskWorkflowDefinition supports '
|
||||
. 'this task. This should not happens.');
|
||||
}
|
||||
|
||||
|
||||
if (0 === $count) {
|
||||
throw new LogicException(sprintf('No taskWorkflowDefinition supports this task type: %s (task id: %s).', $task->getType(), $task->getId()));
|
||||
}
|
||||
|
||||
return $definitions[0];
|
||||
}
|
||||
|
||||
|
||||
public function getWorkflowMetadata(AbstractTask $task, string $key, $metadataSubject = null, ?string $name = null)
|
||||
{
|
||||
return $this->getTaskWorkflowDefinition($task)
|
||||
->getWorkflowMetadata($task, $key, $metadataSubject);
|
||||
}
|
||||
|
||||
public function onTaskStateEntered(Event $e)
|
||||
{
|
||||
$task = $e->getSubject();
|
||||
|
||||
$definition = $this->getTaskWorkflowDefinition($task);
|
||||
|
||||
$task->setClosed($definition->isClosed($task));
|
||||
}
|
||||
|
||||
public function supports(WorkflowInterface $workflow, $subject): bool
|
||||
{
|
||||
if (!$subject instanceof AbstractTask) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return $workflow->getName() === $this
|
||||
->getTaskWorkflowDefinition($subject)->getAssociatedWorkflowName();
|
||||
}
|
||||
|
||||
public function getWorkflowMetadata(AbstractTask $task, string $key, $metadataSubject = null, string $name = null)
|
||||
{
|
||||
return $this->getTaskWorkflowDefinition($task)
|
||||
->getWorkflowMetadata($task, $key, $metadataSubject);
|
||||
}
|
||||
|
||||
public function onTaskStateEntered(Event $e)
|
||||
{
|
||||
$task = $e->getSubject();
|
||||
|
||||
$definition = $this->getTaskWorkflowDefinition($task);
|
||||
|
||||
$task->setClosed($definition->isClosed($task));
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\Migrations\Task;
|
||||
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Create a table for Single task.
|
||||
*/
|
||||
class Version20180413135614 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('DROP SCHEMA chill_task CASCADE');
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
@@ -27,17 +38,5 @@ class Version20180413135614 extends AbstractMigration
|
||||
$this->addSql('ALTER TABLE chill_task.single_task ADD CONSTRAINT FK_194CB3D859EC7D60 FOREIGN KEY (assignee_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_task.single_task ADD CONSTRAINT FK_194CB3D8217BBB47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_task.single_task ADD CONSTRAINT FK_194CB3D870EE2FF6 FOREIGN KEY (circle_id) REFERENCES scopes (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('DROP SCHEMA chill_task CASCADE');
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\Migrations\Task;
|
||||
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Create recurring task
|
||||
* Create recurring task.
|
||||
*/
|
||||
class Version20180413201023 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_task.single_task DROP CONSTRAINT FK_194CB3D840868C31');
|
||||
$this->addSql('DROP SEQUENCE chill_task.recurring_task_id_seq CASCADE');
|
||||
|
||||
$this->addSql('DROP TABLE chill_task.recurring_task');
|
||||
$this->addSql('ALTER TABLE chill_task.single_task DROP recurringTask_id');
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
@@ -30,20 +45,5 @@ class Version20180413201023 extends AbstractMigration
|
||||
$this->addSql('ALTER TABLE chill_task.single_task ADD recurringTask_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE chill_task.single_task ADD CONSTRAINT FK_194CB3D840868C31 FOREIGN KEY (recurringTask_id) REFERENCES chill_task.recurring_task (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_194CB3D840868C31 ON chill_task.single_task (recurringTask_id)');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_task.single_task DROP CONSTRAINT FK_194CB3D840868C31');
|
||||
$this->addSql('DROP SEQUENCE chill_task.recurring_task_id_seq CASCADE');
|
||||
|
||||
$this->addSql('DROP TABLE chill_task.recurring_task');
|
||||
$this->addSql('ALTER TABLE chill_task.single_task DROP recurringTask_id');
|
||||
}
|
||||
}
|
||||
|
@@ -1,23 +1,24 @@
|
||||
<?php declare(strict_types = 1);
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Task;
|
||||
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Add a column 'closed' on tasks
|
||||
* Add a column 'closed' on tasks.
|
||||
*/
|
||||
class Version20180426093011 extends AbstractMigration
|
||||
{
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_task.single_task ADD closed BOOLEAN DEFAULT \'false\' NOT NULL');
|
||||
$this->addSql('ALTER TABLE chill_task.recurring_task ADD closed BOOLEAN DEFAULT \'false\' NOT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
@@ -25,4 +26,12 @@ class Version20180426093011 extends AbstractMigration
|
||||
$this->addSql('ALTER TABLE chill_task.recurring_task DROP closed');
|
||||
$this->addSql('ALTER TABLE chill_task.single_task DROP closed');
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('ALTER TABLE chill_task.single_task ADD closed BOOLEAN DEFAULT \'false\' NOT NULL');
|
||||
$this->addSql('ALTER TABLE chill_task.recurring_task ADD closed BOOLEAN DEFAULT \'false\' NOT NULL');
|
||||
}
|
||||
}
|
||||
|
@@ -1,15 +1,32 @@
|
||||
<?php declare(strict_types = 1);
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Task;
|
||||
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Create task place events table.
|
||||
*/
|
||||
class Version20180502194119 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('DROP SEQUENCE chill_task.single_task_place_event_id_seq CASCADE');
|
||||
$this->addSql('DROP TABLE chill_task.single_task_place_event');
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
@@ -22,13 +39,4 @@ class Version20180502194119 extends AbstractMigration
|
||||
$this->addSql('ALTER TABLE chill_task.single_task_place_event ADD CONSTRAINT FK_D459EBEEF675F31B FOREIGN KEY (author_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE chill_task.single_task_place_event ADD CONSTRAINT FK_D459EBEE8DB60186 FOREIGN KEY (task_id) REFERENCES chill_task.single_task (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('DROP SEQUENCE chill_task.single_task_place_event_id_seq CASCADE');
|
||||
$this->addSql('DROP TABLE chill_task.single_task_place_event');
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Task;
|
||||
|
||||
@@ -6,10 +15,18 @@ use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Add index on single_task_place_events
|
||||
* Add index on single_task_place_events.
|
||||
*/
|
||||
final class Version20181113161925 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('DROP INDEX transition_task_date');
|
||||
$this->addSql('DROP INDEX transition_task');
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
@@ -17,13 +34,4 @@ final class Version20181113161925 extends AbstractMigration
|
||||
$this->addSql('CREATE INDEX transition_task_date ON chill_task.single_task_place_event (task_id, transition, datetime)');
|
||||
$this->addSql('CREATE INDEX transition_task ON chill_task.single_task_place_event (task_id, transition)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('DROP INDEX transition_task_date');
|
||||
$this->addSql('DROP INDEX transition_task');
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Task;
|
||||
|
||||
@@ -6,18 +15,10 @@ use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Add index on single task
|
||||
* Add index on single task.
|
||||
*/
|
||||
final class Version20181113164108 extends AbstractMigration
|
||||
{
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('CREATE INDEX by_type ON chill_task.single_task (type)');
|
||||
$this->addSql('CREATE INDEX by_current_state ON chill_task.single_task USING GIN (current_states)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
@@ -25,4 +26,12 @@ final class Version20181113164108 extends AbstractMigration
|
||||
$this->addSql('DROP INDEX by_type');
|
||||
$this->addSql('DROP INDEX by_current_state');
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
|
||||
|
||||
$this->addSql('CREATE INDEX by_type ON chill_task.single_task (type)');
|
||||
$this->addSql('CREATE INDEX by_current_state ON chill_task.single_task USING GIN (current_states)');
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Task;
|
||||
@@ -12,22 +19,6 @@ use Doctrine\Migrations\AbstractMigration;
|
||||
*/
|
||||
final class Version20210909153533 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
|
||||
$this->addSql('ALTER TABLE chill_task.recurring_task ADD course_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE chill_task.recurring_task ADD CONSTRAINT FK_9F663B90591CC992 FOREIGN KEY (course_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_9F663B90591CC992 ON chill_task.recurring_task (course_id)');
|
||||
$this->addSql('ALTER TABLE chill_task.single_task ADD course_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE chill_task.single_task ADD CONSTRAINT FK_194CB3D8591CC992 FOREIGN KEY (course_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_194CB3D8591CC992 ON chill_task.single_task (course_id)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE chill_task.recurring_task DROP CONSTRAINT FK_9F663B90591CC992');
|
||||
@@ -35,4 +26,19 @@ final class Version20210909153533 extends AbstractMigration
|
||||
$this->addSql('ALTER TABLE chill_task.single_task DROP CONSTRAINT FK_194CB3D8591CC992');
|
||||
$this->addSql('ALTER TABLE chill_task.single_task DROP course_id');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE chill_task.recurring_task ADD course_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE chill_task.recurring_task ADD CONSTRAINT FK_9F663B90591CC992 FOREIGN KEY (course_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_9F663B90591CC992 ON chill_task.recurring_task (course_id)');
|
||||
$this->addSql('ALTER TABLE chill_task.single_task ADD course_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE chill_task.single_task ADD CONSTRAINT FK_194CB3D8591CC992 FOREIGN KEY (course_id) REFERENCES chill_person_accompanying_period (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_194CB3D8591CC992 ON chill_task.single_task (course_id)');
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Task;
|
||||
@@ -9,6 +16,11 @@ use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20211029213909 extends AbstractMigration
|
||||
{
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP INDEX chill_task.by_end_date');
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add index for task state and end date';
|
||||
@@ -18,9 +30,4 @@ final class Version20211029213909 extends AbstractMigration
|
||||
{
|
||||
$this->addSql('CREATE INDEX by_end_date ON chill_task.single_task (end_date DESC NULLS FIRST)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP INDEX chill_task.by_end_date');
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user