Compare commits

...

38 Commits

Author SHA1 Message Date
868a94d66b #78 title 'services' replaced by 'services concernes' 2021-10-04 16:26:28 +02:00
3d35faede9 #70 'par ' replaced by 'referent: ' 2021-10-04 16:24:08 +02:00
e9d485f814 fix error when displaying list of Person tasks. 2021-10-04 16:12:59 +02:00
b3b54486b5 improvements following review 2021-10-01 16:32:05 +02:00
461b96ea37 duplicate templates deleted 2021-10-01 11:32:46 +02:00
fc237db98a fix: wrong template rendered for new course task 2021-10-01 11:32:32 +02:00
43daab1f7b transition button added to listitems of course tasks 2021-10-01 11:32:05 +02:00
85e8fe59a4 changes to TaskVoter reinstated, mistake made in conflict resolvement 2021-10-01 11:18:10 +02:00
262fefa92f access granted condition lifted temporarily to show the 'add new task' button on index page for Person 2021-10-01 10:52:05 +02:00
1403ee2ba5 redirect after submit bug fixed for edit and delete when coming from show.html.twig 2021-10-01 10:49:55 +02:00
548247188f temporarily lifted scope validation, currently no scopes in select list to be selected 2021-10-01 10:49:07 +02:00
6a34046e93 comments deleted, notations adjusted, ... 2021-10-01 10:47:32 +02:00
88b8ff86d1 reorganize templates for a better structure 2021-10-01 10:45:04 +02:00
cc258ba164 templates added for transition of course task and taskcontroller adapted to show correct template 2021-09-30 14:24:17 +02:00
5d69e48787 more conformity between URL's single-task 2021-09-30 14:22:20 +02:00
cb4059e5c3 comment out use of voter for menu entry, new way of handling rights? 2021-09-28 14:37:27 +02:00
8411c909ff notation modifications 2021-09-28 12:55:46 +02:00
41fc41b1da migration namespace adjusted 2021-09-28 12:55:46 +02:00
cb5b45cbe8 autowire and configure MenuBuilder 2021-09-28 12:55:46 +02:00
4c47a35457 Fix SingleTaskListType
accompanyingCourse also defined as a form option
2021-09-28 12:55:46 +02:00
34dd35f2e2 Changed name of PersonMenuBuilder
More general name since it also contains the AccompanyingPeriod task menu entry now.
2021-09-28 12:55:46 +02:00
6e3ce06fcf new/show/edit/delete/list functionality added for accompanyingperiod task 2021-09-28 12:55:46 +02:00
537518b66f controller and templates adapted to display list of accompanying period tasks + detailpage of task 2021-09-28 12:53:24 +02:00
4ad9714e8b Menu entry added in PersonMenuBuilder of taskbundle. Removed from menubuilder in Personbundle. Rename file, PersonMenuBuilder to MenuBuilder? 2021-09-28 12:53:23 +02:00
5a936cd20b templates + controller further adapted to work with accompanyingCourse. new and show methods don't work yet due to authorization/voter issues
templates adapted for use with accompanyingCourse tasks also
2021-09-28 12:53:23 +02:00
1fb14834b7 tasks added to accompanyingCourse menu 2021-09-28 12:53:23 +02:00
53fc5b8399 adaptation of newTask() method in singleTaskController 2021-09-28 12:49:38 +02:00
8318458805 relation between task and accompanyingcourse created 2021-09-28 12:49:38 +02:00
7515415888 autowire and configure MenuBuilder 2021-09-17 15:18:48 +02:00
27a52ce166 Fix SingleTaskListType
accompanyingCourse also defined as a form option
2021-09-17 14:53:15 +02:00
17b6f287dc Changed name of PersonMenuBuilder
More general name since it also contains the AccompanyingPeriod task menu entry now.
2021-09-17 14:49:43 +02:00
01ae50aca7 new/show/edit/delete/list functionality added for accompanyingperiod task 2021-09-17 14:48:29 +02:00
a156bd0863 controller and templates adapted to display list of accompanying period tasks + detailpage of task 2021-09-16 15:55:09 +02:00
b1dbd8b011 Menu entry added in PersonMenuBuilder of taskbundle. Removed from menubuilder in Personbundle. Rename file, PersonMenuBuilder to MenuBuilder? 2021-09-15 11:13:09 +02:00
b28b4e5fba templates + controller further adapted to work with accompanyingCourse. new and show methods don't work yet due to authorization/voter issues
templates adapted for use with accompanyingCourse tasks also
2021-09-10 15:19:59 +02:00
ead96f3836 tasks added to accompanyingCourse menu 2021-09-10 14:57:41 +02:00
27077c9d96 adaptation of newTask() method in singleTaskController 2021-09-09 18:21:41 +02:00
8a38712525 relation between task and accompanyingcourse created 2021-09-09 17:43:35 +02:00
35 changed files with 1291 additions and 799 deletions

View File

@@ -28,6 +28,7 @@ use Chill\MainBundle\Entity\HasCentersInterface;
use Chill\MainBundle\Entity\HasScopesInterface;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\Center;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
@@ -977,6 +978,15 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
return $this->addressLocation;
}
public function getCenter(): ?Center
{
if (count($this->getPersons()) === 0){
return null;
} else {
return $this->getPersons()->first()->getCenter();
}
}
/**
* @Groups({"write"})
*/

View File

@@ -66,7 +66,7 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
'routeParameters' => [
'id' => $period->getId()
]])
->setExtras(['order' => 50]);
->setExtras(['order' => 40]);
}

View File

@@ -21,7 +21,7 @@ const appMessages = {
active: "En file active"
},
open_at: "ouvert le ",
by: "par ",
by: "Référent: ",
emergency: "urgent",
confidential: "confidentiel",
regular: "régulier",
@@ -87,7 +87,7 @@ const appMessages = {
no_address: "Il n'y a pas d'adresse associée au parcours"
},
scopes: {
title: "Services",
title: "Services concernés",
add_at_least_one: "Indiquez au moins un service",
},
referrer: {

View File

@@ -6,7 +6,6 @@ use Chill\PersonBundle\Privacy\PrivacyEvent;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Doctrine\ORM\EntityManager;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -20,15 +19,18 @@ use Symfony\Component\Security\Core\Role\Role;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\TaskBundle\Repository\SingleTaskRepository;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\CenterRepository;
use Chill\MainBundle\Repository\ScopeRepository;
use Chill\MainBundle\Repository\UserRepository;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\MainBundle\Entity\UserRepository;
use Chill\TaskBundle\Event\TaskEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Chill\TaskBundle\Event\UI\UIEvent;
use Chill\MainBundle\Repository\CenterRepository;
use Chill\MainBundle\Timeline\TimelineBuilder;
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Symfony\Contracts\Translation\TranslatorInterface as TranslationTranslatorInterface;
/**
* Class SingleTaskController
@@ -37,22 +39,23 @@ use Chill\MainBundle\Timeline\TimelineBuilder;
*/
class SingleTaskController extends AbstractController
{
protected EventDispatcherInterface $eventDispatcher;
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
protected TimeLineBuilder $timelineBuilder;
/**
*
* @var TimelineBuilder
*/
protected $timelineBuilder;
/**
* @var LoggerInterface
*/
protected $logger;
protected LoggerInterface $logger;
protected PersonRepository $personRepository;
protected AccompanyingPeriodRepository $courseRepository;
protected UserRepository $userRepository;
protected CenterRepository $centerRepository;
protected ScopeRepository $scopeRepository;
protected SingleTaskRepository $taskRepository;
/**
* SingleTaskController constructor.
@@ -62,11 +65,35 @@ class SingleTaskController extends AbstractController
public function __construct(
EventDispatcherInterface $eventDispatcher,
TimelineBuilder $timelineBuilder,
LoggerInterface $logger
LoggerInterface $logger,
SingleTaskRepository $singleTaskRepository,
PersonRepository $personRepository,
AccompanyingPeriodRepository $courseRepository,
UserRepository $userRepository,
CenterRepository $centerRepository,
ScopeRepository $scopeRepository
) {
$this->eventDispatcher = $eventDispatcher;
$this->timelineBuilder = $timelineBuilder;
$this->logger = $logger;
$this->taskRepository = $singleTaskRepository;
$this->scopeRepository = $scopeRepository;
$this->centerRepository = $centerRepository;
$this->userRepository = $userRepository;
$this->personRepository = $personRepository;
$this->courseRepository = $courseRepository;
}
private function getEntityContext(Request $request)
{
if($request->query->has('person_id')){
return 'person';
} else if ($request->query->has('course_id')) {
return 'course';
} else {
return null;
}
}
@@ -77,40 +104,58 @@ class SingleTaskController extends AbstractController
* )
*/
public function newAction(
Request $request,
TranslatorInterface $translator
TranslationTranslatorInterface $translator,
Request $request
) {
$task = (new SingleTask())
->setAssignee($this->getUser())
->setType('task_default')
;
$entityType = $this->getEntityContext($request);
if ($request->query->has('person_id')) {
if ($entityType !== null) {
$personId = $request->query->getInt('person_id', 0); // sf4 check:
$entityId = $request->query->getInt("{$entityType}_id", 0); // sf4 check:
// prevent error: `Argument 2 passed to ::getInt() must be of the type int, null given`
if ($personId === null) {
return new Response("You must provide a person_id", Response::HTTP_BAD_REQUEST);
switch($entityType){
case 'person':
$person = $this->personRepository
->find($entityId);
if ($person === null) {
$this->createNotFoundException("Invalid person id");
}
$task->setPerson($person);
break;
case 'course':
$course = $this->courseRepository
->find($entityId);
if($course === null) {
$this->createNotFoundException("Invalid accompanying course id");
}
$task->setCourse($course);
break;
default:
new Response("You must provide a {$entityType}_id", Response::HTTP_BAD_REQUEST);
break;
}
$person = $this->getDoctrine()->getManager()
->getRepository(Person::class)
->find($personId);
if ($person === null) {
$this->createNotFoundException("Invalid person id");
}
$task->setPerson($person);
}
$this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not '
. 'allowed to create this task');
//TODO : resolve access rights
// $this->denyAccessUnlessGranted(TaskVoter::CREATE, $task, 'You are not '
// . 'allowed to create this task');
$form = $this->setCreateForm($task, new Role(TaskVoter::CREATE));
$form->handleRequest($request);
if ($form->isSubmitted()) {
@@ -124,21 +169,42 @@ class SingleTaskController extends AbstractController
$this->addFlash('success', $translator->trans("The task is created"));
return $this->redirectToRoute('chill_task_singletask_list', [
'person_id' => $task->getPerson()->getId()
]);
switch($entityType){
case 'person':
return $this->redirectToRoute('chill_task_singletask_list', [
'person_id' => $task->getPerson()->getId()
]);
break;
case 'course':
return $this->redirectToRoute('chill_task_singletask_courselist', [
'course_id' => $task->getCourse()->getId()
]);
break;
}
} else {
$this->addFlash('error', $translator->trans("This form contains errors"));
}
}
return $this->render('ChillTaskBundle:SingleTask:new.html.twig', array(
'form' => $form->createView(),
'task' => $task
));
}
switch($this->getEntityContext($request)){
case 'person':
return $this->render('@ChillTask/SingleTask/Person/new.html.twig', array(
'form' => $form->createView(),
'task' => $task,
'person' => $person,
));
break;
case 'course':
return $this->render('@ChillTask/SingleTask/AccompanyingCourse/new.html.twig', array(
'form' => $form->createView(),
'task' => $task,
'accompanyingCourse' => $course,
));
break;
}
}
/**
* @Route(
@@ -146,48 +212,58 @@ class SingleTaskController extends AbstractController
* name="chill_task_single_task_show"
* )
*/
public function showAction(Request $request, $id)
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
$task = $em->getRepository(SingleTask::class)->find($id);
if ($task->getPerson() !== null) {
$personId = $task->getPerson()->getId();
if ($personId === null) {
return new Response("You must provide a person_id", Response::HTTP_BAD_REQUEST);
}
$person = $this->getDoctrine()->getManager()
->getRepository(Person::class)
->find($personId);
if ($person === null) {
throw $this->createNotFoundException("Invalid person id");
}
}
$this->denyAccessUnlessGranted(TaskVoter::SHOW, $task, 'You are not '
. 'allowed to view this task');
// $em = $this->getDoctrine()->getManager();
$task = $this->taskRepository->find($id);
if (!$task) {
throw $this->createNotFoundException('Unable to find Task entity.');
}
$timeline = $this->timelineBuilder
->getTimelineHTML('task', array('task' => $task));
$event = new PrivacyEvent($person, array(
if ($task->getPerson() !== null) {
$person = $task->getPerson();
if ($person === null) {
throw $this->createNotFoundException("Invalid person id");
}
$event = new PrivacyEvent($person, array(
'element_class' => SingleTask::class,
'element_id' => $task->getId(),
'action' => 'show'
));
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
return $this->render('ChillTaskBundle:SingleTask:show.html.twig', array(
'task' => $task,
'timeline' => $timeline
));
));
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
}
if ($task->getCourse() === null)
{
return new Response("You must provide a course_id", Response::HTTP_BAD_REQUEST);
}
$this->denyAccessUnlessGranted(TaskVoter::SHOW, $task, 'You are not '
. 'allowed to view this task');
$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
));
}
}
@@ -198,28 +274,30 @@ class SingleTaskController extends AbstractController
* )
*/
public function editAction(
Request $request,
$id,
TranslatorInterface $translator
TranslationTranslatorInterface $translator,
Request $request
) {
$em = $this->getDoctrine()->getManager();
$task = $em->getRepository(SingleTask::class)->find($id);
if ($task->getPerson() !== null) {
$personId = $task->getPerson()->getId();
if ($personId === null) {
if ($task->getContext() instanceof Person) {
$person = $task->getPerson();
if ($person === null) {
return new Response("You must provide a person_id", Response::HTTP_BAD_REQUEST);
}
$person = $this->getDoctrine()->getManager()
->getRepository(Person::class)
->find($personId);
} else {
if ($person === null) {
throw $this->createNotFoundException("Invalid person id");
$course = $task->getCourse();
if ($course === null) {
throw $this->createNotFoundException("Invalid accompanying period id");
}
}
$this->denyAccessUnlessGranted(TaskVoter::UPDATE, $task, 'You are not '
. 'allowed to edit this task');
@@ -246,19 +324,25 @@ class SingleTaskController extends AbstractController
$this->addFlash('success', $translator
->trans("The task has been updated"));
$event = new PrivacyEvent($person, array(
if($task->getContext() instanceof Person){
$event = new PrivacyEvent($person, array(
'element_class' => SingleTask::class,
'element_id' => $task->getId(),
'action' => 'update'
));
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
return $this->redirectToRoute(
'chill_task_singletask_list',
$request->query->get('list_params', [])
);
));
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
return $this->redirectToRoute(
'chill_task_singletask_list',
$request->query->get('list_params', [])
);
} else {
return $this->redirectToRoute(
'chill_task_singletask_courselist',
$request->query->get('list_params', [])
);
}
} else {
$this->addFlash('error', $translator->trans("This form contains errors"));
}
@@ -270,17 +354,26 @@ class SingleTaskController extends AbstractController
return $event->getResponse();
}
$event = new PrivacyEvent($person, array(
'element_class' => SingleTask::class,
'element_id' => $task->getId(),
'action' => 'edit'
));
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
return $this->render('ChillTaskBundle:SingleTask:edit.html.twig', array(
'task' => $task,
'form' => $form->createView()
));
if($task->getContext() instanceof Person){
$event = new PrivacyEvent($person, 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' => $course
));
}
}
@@ -293,11 +386,9 @@ class SingleTaskController extends AbstractController
public function deleteAction(
Request $request,
$id,
TranslatorInterface $translator
TranslationTranslatorInterface $translator
) {
$em = $this->getDoctrine()->getManager();
$task = $em->getRepository(SingleTask::class)->find($id);
$task = $this->taskRepository->find($id);
if (!$task) {
throw $this->createNotFoundException('Unable to find Task entity.');
@@ -309,18 +400,31 @@ class SingleTaskController extends AbstractController
return new Response("You must provide a person_id", Response::HTTP_BAD_REQUEST);
}
$person = $this->getDoctrine()->getManager()
->getRepository(Person::class)
$person = $this->personRepository
->find($personId);
if ($person === null) {
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);
}
$course = $this->courseRepository
->find($courseId);
if($course === null){
throw $this->createNotFoundException("Invalid accompanying period id");
}
}
$this->denyAccessUnlessGranted(TaskVoter::DELETE, $task, 'You are not '
. 'allowed to delete this task');
// TODO: reactivate right to delete
// $this->denyAccessUnlessGranted(TaskVoter::DELETE, $task, 'You are not '
// . 'allowed to delete this task');
$form = $this->createDeleteForm($id);
@@ -334,10 +438,8 @@ class SingleTaskController extends AbstractController
'task_id' => $task->getId(),
'description' => $task->getDescription(),
'assignee' => $task->getAssignee(),
'scope_id' => $task->getScope()->getId(),
//'start_date' => $task->getStartDate()->format('Y-m-d'),
//'end_date' => $task->getEndDate()->format('Y-m-d'),
//'warning_interval' => $task->getWarningInterval()->format('Y-m-d')
// TODO reimplement scope
// 'scope_id' => $task->getScope()->getId(),
));
$em = $this->getDoctrine()->getManager();
@@ -347,19 +449,35 @@ class SingleTaskController extends AbstractController
$this->addFlash('success', $translator
->trans("The task has been successfully removed."));
return $this->redirect($this->generateUrl(
'chill_task_singletask_list',
$request->query->get('list_params', [
'person_id' => $person->getId()
])));
if($task->getContext() instanceof Person){
return $this->redirect($this->generateUrl(
'chill_task_singletask_list',
$request->query->get('list_params', [
'person_id' => $person->getId()
])));
} else {
return $this->redirect($this->generateUrl(
'chill_task_singletask_courselist',
$request->query->get('list_params', [
'course_id' => $course->getId()
])));
}
}
}
if($task->getContext() instanceof Person){
return $this->render('@ChillTask/SingleTask/Person/confirm_delete.html.twig', array(
'task' => $task,
'delete_form' => $form->createView()
));
} else {
return $this->render('@ChillTask/SingleTask/AccompanyingCourse/confirm_delete.html.twig', array(
'task' => $task,
'delete_form' => $form->createView(),
'accompanyingCourse' => $course
));
}
return $this->render('ChillTaskBundle:SingleTask:confirm_delete.html.twig', array(
'task' => $task,
'delete_form' => $form->createView()
));
}
/**
@@ -372,7 +490,7 @@ class SingleTaskController extends AbstractController
{
$form = $this->createForm(SingleTaskType::class, $task, [
'center' => $task->getCenter(),
'role' => $role
'role' => $role,
]);
$form->add('submit', SubmitType::class);
@@ -388,7 +506,7 @@ class SingleTaskController extends AbstractController
* name="chill_task_single_my_tasks"
* )
*/
public function myTasksAction(TranslatorInterface $translator)
public function myTasksAction(TranslationTranslatorInterface $translator)
{
return $this->redirectToRoute('chill_task_singletask_list', [
'user_id' => $this->getUser()->getId(),
@@ -402,22 +520,21 @@ class SingleTaskController extends AbstractController
* Arguments:
* - user_id
* - scope_id
* - course_id
* - person_id
* - hide_form (hide the form to filter the tasks)
* - status: date state, amongst SingleTaskRepository::DATE_STATUSES, or 'closed'
*
* @Route(
* "/{_locale}/task/singletask/list",
* "/{_locale}/task/single-task/list",
* name="chill_task_singletask_list"
* )
*/
public function listAction(
Request $request,
PaginatorFactory $paginatorFactory,
SingleTaskRepository $taskRepository,
PersonRepository $personRepository,
CenterRepository $centerRepository,
FormFactoryInterface $formFactory
// SingleTaskRepository $taskRepository,
FormFactoryInterface $formFactory,
Request $request
) {
/* @var $viewParams array The parameters for the view */
/* @var $params array The parameters for the query */
@@ -428,13 +545,13 @@ class SingleTaskController extends AbstractController
$params['user'] = null;
$viewParams['center'] = null;
$params['types'] = null;
$viewParams['accompanyingCourse'] = null;
$params['accompanyingCourse'] = null;
// Get parameters from url
if (!empty($request->query->get('person_id', NULL))) {
$personId = $request->query->getInt('person_id', 0);
$person = $personRepository->find($personId);
$person = $this->personRepository->find($personId);
if ($person === null) {
throw $this->createNotFoundException("This person ' $personId ' does not exist.");
@@ -444,9 +561,23 @@ class SingleTaskController extends AbstractController
$viewParams['person'] = $person;
$params['person'] = $person;
}
if (!empty($request->query->get('course_id', NULL))) {
$courseId = $request->query->getInt('course_id', 0);
$course = $this->courseRepository->find($courseId);
if ($course === null) {
throw $this->createNotFoundException("This accompanying course ' $courseId ' does not exist.");
}
$this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $course);
$viewParams['accompanyingCourse'] = $course;
$params['accompanyingCourse'] = $course;
}
if (!empty($request->query->get('center_id', NULL))) {
$center = $centerRepository->find($request->query->getInt('center_id'));
$center = $this->centerRepository->find($this->request->query->getInt('center_id'));
if ($center === null) {
throw $this->createNotFoundException('center not found');
}
@@ -454,7 +585,7 @@ class SingleTaskController extends AbstractController
}
if(!empty($request->query->get('types', []))) {
$types = $request->query->get('types', []);
$types = $this->request->query->get('types', []);
if (count($types) > 0) {
$params['types'] = $types;
}
@@ -467,7 +598,7 @@ class SingleTaskController extends AbstractController
$userId = $request->query->getInt('user_id', 0);
$user = $this->getDoctrine()->getManager()
->getRepository('ChillMainBundle:User')
->getRepository(User::class)
->find($userId);
if ($user === null) {
@@ -483,8 +614,7 @@ class SingleTaskController extends AbstractController
$scopeId = $request->query->getInt('scope_id', 0);
$scope = $this->getDoctrine()->getManager()
->getRepository('ChillMainBundle:Scope')
$scope = $this->scopeRepository
->find($scopeId);
if ($scope === null) {
@@ -495,11 +625,9 @@ class SingleTaskController extends AbstractController
$params['scope'] = $scope;
}
// collect parameters for filter
$possibleStatuses = \array_merge(SingleTaskRepository::DATE_STATUSES, [ 'closed' ]);
$statuses = $request->query->get('status', $possibleStatuses);
// check for invalid statuses
$diff = \array_diff($statuses, $possibleStatuses);
if (count($diff) > 0) {
return new Response(
@@ -518,7 +646,6 @@ class SingleTaskController extends AbstractController
continue;
}
// different query if regarding to date or 'closed'
if (in_array($status, SingleTaskRepository::DATE_STATUSES)) {
$params['date_status'] = $status;
$params['is_closed'] = false;
@@ -527,14 +654,14 @@ class SingleTaskController extends AbstractController
$params['is_closed'] = true;
}
$count = $taskRepository
$count = $this->taskRepository
->countByParameters($params, $this->getUser())
;
$paginator = $paginatorFactory->create($count);
$viewParams['single_task_'.$status.'_count'] = $count;
$viewParams['single_task_'.$status.'_paginator'] = $paginator;
$viewParams['single_task_'.$status.'_tasks'] = $taskRepository
$viewParams['single_task_'.$status.'_tasks'] = $this->taskRepository
->findByParameters($params, $this->getUser(),
$singleStatus ? $paginator->getCurrentPage()->getFirstItemNumber() : 0,
$singleStatus ? $paginator->getItemsPerPage() : 10)
@@ -543,16 +670,16 @@ class SingleTaskController extends AbstractController
$tasks_count = $tasks_count + $count;
}
// total number of tasks
$viewParams['tasks_count'] = $tasks_count;
if ($viewParams['person'] !== null){
$viewParams['layout'] = '@ChillPerson/Person/layout.html.twig';
} else if ($viewParams['accompanyingCourse'] !== null){
$viewParams['layout'] = '@ChillPerson/AccompanyingCourse/layout.html.twig';
} else {
$viewParams['layout'] = '@ChillMain/layout.html.twig';
}
// Form for filtering tasks
$form = $formFactory->createNamed(null, SingleTaskListType::class, null, [
'person' => $viewParams['person'],
'method' => Request::METHOD_GET,
@@ -570,14 +697,14 @@ class SingleTaskController extends AbstractController
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
}
return $this->render('ChillTaskBundle:SingleTask:index.html.twig',
\array_merge($viewParams, [ 'form' => $form->createView() ]));
return $this->render('@ChillTask/SingleTask/index.html.twig',
array_merge($viewParams, [ 'form' => $form->createView() ]));
}
protected function getPersonParam(Request $request, EntityManagerInterface $em)
protected function getPersonParam(Request $request)
{
$person = $em->getRepository(Person::class)
$person = $this->personRepository
->find($request->query->getInt('person_id'))
;
@@ -591,9 +718,9 @@ class SingleTaskController extends AbstractController
return $person;
}
protected function getUserParam(Request $request, EntityManagerInterface $em)
protected function getUserParam(Request $request)
{
$user = $em->getRepository(User::class)
$user = $this->userRepository
->find($request->query->getInt('user_id'))
;
@@ -623,4 +750,55 @@ class SingleTaskController extends AbstractController
;
}
/**
* @Route(
* "/{_locale}/task/single-task/courselist",
* name="chill_task_singletask_courselist")
*/
public function listCourseTasks(
FormFactoryInterface $formFactory,
TranslationTranslatorInterface $translator,
Request $request
): Response
{
if (!empty($request->query->get('course_id', NULL))) {
$courseId = $request->query->getInt('course_id', 0);
$course = $this->courseRepository->find($courseId);
if ($course === null) {
throw $this->createNotFoundException("This accompanying course ' $courseId ' does not exist.");
}
}
if($course === NULL) {
throw $this->createNotFoundException('Accompanying course not found');
}
$tasks = $this->taskRepository
->findBy(
array('course' => $course)
);
$form = $formFactory->createNamed(null, SingleTaskListType::class, null, [
'accompanyingCourse' => $course,
'method' => Request::METHOD_GET,
'csrf_protection' => false,
'add_type' => true
]);
return $this->render(
'@ChillTask/SingleTask/index.html.twig',
[
'tasks' => $tasks,
'accompanyingCourse' => $course,
'layout' => '@ChillPerson/AccompanyingCourse/layout.html.twig',
'form' => $form->createView(),
'title' => $translator->trans('Tasks for this accompanying period')
]);
}
}

View File

@@ -64,7 +64,7 @@ class TaskController extends AbstractController
'id' => $task->getId(),
'list_params' => $request->query->get('list_params', [])
]);
$defaultTemplate = '@ChillTask/SingleTask/transition.html.twig';
$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",

View File

@@ -10,6 +10,7 @@ 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\PersonBundle\Entity\AccompanyingPeriod;
/**
* AbstractTask
@@ -67,9 +68,16 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface
* @ORM\ManyToOne(
* targetEntity="\Chill\PersonBundle\Entity\Person"
* )
* @Assert\NotNull()
*/
private $person;
/**
* @var AccompanyingPeriod
* @ORM\ManyToOne(targetEntity="\Chill\PersonBundle\Entity\AccompanyingPeriod")
*/
private $course;
/**
*
@@ -77,7 +85,6 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface
* @ORM\ManyToOne(
* targetEntity="\Chill\MainBundle\Entity\Scope"
* )
* @Assert\NotNull()
*/
private $circle;
@@ -202,6 +209,11 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface
return $this->person;
}
public function getCourse(): ?AccompanyingPeriod
{
return $this->course;
}
public function getCircle(): ?Scope
{
return $this->circle;
@@ -219,6 +231,12 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface
return $this;
}
public function setCourse(AccompanyingPeriod $course)
{
$this->course = $course;
return $this;
}
public function setCircle(Scope $circle)
{
$this->circle = $circle;
@@ -229,11 +247,23 @@ abstract class AbstractTask implements HasScopeInterface, HasCenterInterface
{
if ($this->getPerson() instanceof Person) {
return $this->getPerson()->getCenter();
} else {
return $this->getCourse()->getCenter();
}
return null;
}
public function getContext()
{
// if ($this->getCourse() instanceof AccompanyingPeriod){
// return $this->getCourse();
// } else {
// return $this->getPerson();
// }
return $this->getPerson() ?? $this->getCourse();
}
public function getScope(): ?\Chill\MainBundle\Entity\Scope
{

View File

@@ -284,6 +284,7 @@ class SingleTaskListType extends AbstractType
->setDefined('person')
->setDefault('person', null)
->setAllowedTypes('person', [Person::class, 'null'])
->setDefined('accompanyingCourse')
->setDefined('add_status')
->setDefault('add_status', false)
->setAllowedTypes('add_status', ['bool'])

View File

@@ -48,11 +48,12 @@ class SingleTaskType extends AbstractType
'center' => $options['center'],
'role' => $options['role'],
'placeholder' => 'Not assigned'
])
])
->add('circle', ScopePickerType::class, [
'center' => $options['center'],
'role' => $options['role']
])
'role' => $options['role'],
'required' => false
])
->add('startDate', ChillDateType::class, [
'required' => false
])
@@ -61,8 +62,7 @@ class SingleTaskType extends AbstractType
])
->add('warningInterval', DateIntervalType::class, [
'required' => false
])
;
]);
}
public function configureOptions(OptionsResolver $resolver)

View File

@@ -28,7 +28,7 @@ use Symfony\Component\Translation\TranslatorInterface;
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class PersonMenuBuilder implements LocalMenuBuilderInterface
class MenuBuilder implements LocalMenuBuilderInterface
{
/**
*
@@ -53,28 +53,55 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
/* @var $person \Chill\PersonBundle\Entity\Person */
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'), [
'route' => 'chill_task_singletask_list',
'routeParameters' => $menuId === 'person' ?
'routeParameters' =>
[ 'person_id' => $person->getId() ]
:
null,
])
->setExtra('order', 400)
;
if ($menuId === 'section') {
$menu->setExtra('icons', 'tasks');
}
])
->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_courselist',
'routeParameters' =>
[ 'course_id' => $course->getId() ]
])
->setExtra('order', 400);
// }
}
public static function getMenuIds(): array
{
return ['person'];
return ['person', 'accompanyingCourse'];
}
}

View File

@@ -0,0 +1,19 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = 'chill_task_task_list' %}
{% set course = task.course %}
{% block title 'Remove task'|trans %}
{% block content %}
{{ include('@ChillMain/Util/confirmation_template.html.twig',
{
'title' : 'Remove task'|trans,
'confirm_question' : 'Are you sure you want to remove the task about accompanying period "%id%" ?'|trans({ '%id%' : course.id } ),
'cancel_route' : 'chill_task_singletask_courselist',
'cancel_parameters' : app.request.query.get('list_params', { } ),
'form' : delete_form,
} ) }}
{% endblock %}

View File

@@ -0,0 +1,15 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = 'chill_task_single_task_edit' %}
{% set course = task.course %}
{% block title %}
{{ 'Edit task'|trans }}
{% endblock %}
{% block content %}
{% include '@ChillTask/SingleTask/_edit.html.twig' %}
{% endblock %}

View File

@@ -0,0 +1,106 @@
{% if tasks|length > 0 %}
<h3>{{ title|trans }}</h3>
<table class="table table-bordered border-dark chill-task-list">
<tbody>
{% for task in tasks %}
<tr>
<td>
<div>
<span class="chill-task-list__row__title">{{ task.title }}</span>
</div>
<div>
<span class="chill-task-list__row__type">{{ task_workflow_metadata(task, 'definition.name')|trans }}</span>
</div>
<div>
{% for place in workflow_marked_places(task) %}
<span class="task-status box type-{{ task.type }} place-{{ place }}">{{ place|trans }}</span>
{% endfor %}
{% if task.assignee is not null %}
<div class="chill-task-list__row__assignee">
<span class="chill_task-list__row__assignee_by">{{ 'By'|trans }}&nbsp;:</span>
{{ task.assignee.username }}</div>
{% endif %}
</div>
{% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %}
<div class="chill-task-list__row__dates">
<ul class="record_actions column">
{% if task.startDate is not null %}
<li title="{{ 'Start'|trans|escape('html_attr') }}">
<i class="fa fa-play"></i>
{{ task.startDate|format_date('medium') }}
</li>
{% endif %}
{% if task.warningDate is not null %}
<li title="{{ 'Warning'|trans|escape('html_attr') }}">
<i class="fa fa-exclamation-triangle"></i>
{{ task.warningDate|format_date('medium') }}
</li>
{% endif %}
{% if task.endDate is not null %}
<li title="{{ 'End'|trans|escape('html_attr') }}">
<i class="fa fa-hourglass-end"></i>
{{ task.endDate|format_date('medium') }}
</li>
{% endif %}
</ul>
</div>
{% endif %}
</td>
<td>
<ul class="record_actions">
{% if workflow_transitions(task)|length > 0 %}
<li>
<div class="btn-group">
<a class="btn btn-task-exchange dropdown-toggle" href="#" role="button" id="taskExchange" data-bs-toggle="dropdown" aria-expanded="false">
{{'Change task status'|trans}}
</a>
<ul class="dropdown-menu" aria-labelledby="taskExchange">
{% for transition in workflow_transitions(task) %}
<li>
<a class="dropdown-item" href="{{ path('chill_task_task_transition', { 'taskId': task.id, 'transition': transition.name, 'kind': 'single-task', 'list_params': app.request.query.all }) }}" class="{{ task_workflow_metadata(task, 'transition.class', transition)|e('html_attr') }}">
{{ task_workflow_metadata(task, 'transition.verb', transition)|trans }}
</a>
</li>
{% endfor %}
</ul>
</div>
</li>
{% endif %}
<li>
<a href="{{ path('chill_task_single_task_show', { 'id': task.id, 'list_params': app.request.query.all }) }}" class="btn btn-show "></a>
</li>
{# {% if is_granted('CHILL_TASK_TASK_UPDATE', task) %} #}
<li>
<a href="{{ path('chill_task_single_task_edit', { 'id': task.id, 'list_params': app.request.query.all }) }}" class="btn btn-update "></a>
</li>
{# {% endif %} #}
{# {% if is_granted('CHILL_TASK_TASK_DELETE', task) %} #}
<li>
<a href="{{ path('chill_task_single_task_delete', { 'id': task.id, 'list_params': app.request.query.all } ) }}" class="btn btn-delete "></a>
</li>
{# {% endif %} #}
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
<ul class="record_actions">
<li>
{% if accompanyingCourse is not null %}
<a href="{{ path('chill_task_single_task_new', {'course_id': accompanyingCourse.id}) }}" class="btn btn-create">
{{ 'Add a new task' | trans }}
</a>
{% endif %}
</li>
</ul>

View File

@@ -0,0 +1,13 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = 'chill_task_single_task_new' %}
{# {% set person = task.person %} #}
{% block title %}
{{ 'New task'|trans }}
{% endblock %}
{% block content %}
{% include '@ChillTask/SingleTask/_new.html.twig' %}
{% endblock %}

View File

@@ -0,0 +1,17 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = 'chill_task_single_task_show' %}
{% set accompanyingCourse = task.course %}
{% block title %}
{{ 'Task'|trans }}
{% endblock %}
{% block content %}
{% include '@ChillTask/SingleTask/_show.html.twig' %}
{% endblock %}

View File

@@ -0,0 +1,12 @@
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = 'chill_task_task_list' %}
{% set accompanyingCourse = task.course %}
{% block title 'Remove task'|trans %}
{% block content %}
{% include '@ChillTask/SingleTask/_transition.html.twig' %}
{% endblock %}

View File

@@ -0,0 +1,31 @@
{#
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% extends person is defined ? "@ChillPerson/Person/layout.html.twig" %}
{% set activeRouteKey = 'chill_task_single_task_edit' %}
{% set person = task.person %}
{% block title %}
{{ 'Edit task'|trans }}
{% endblock %}
{% block personcontent %}
{% include '@ChillTask/SingleTask/_edit.html.twig' %}
{% endblock %}

View File

@@ -0,0 +1,257 @@
{% macro date_status(title, tasks, count, paginator, status, isSingleStatus, person, user) %}
{% if tasks|length > 0 %}
<h3>{{ title|trans }}</h3>
<table class="table table-bordered border-dark chill-task-list">
<tbody>
{% for task in tasks %}
<tr>
<td>
<div>
<span class="chill-task-list__row__title">{{ task.title }}</span>
</div>
{% if person is null %}
<div>
<span class="chill-task-list__row__person-for">{{ 'For person'|trans }}&nbsp;:</span>
<span class="chill-task-list__row__person">
<a href="{{ path('chill_person_view', {person_id : task.person.Id}) }}">{{ task.person}}</a>
</span>
</div>
{% endif %}
<div>
<span class="chill-task-list__row__type">{{ task_workflow_metadata(task, 'definition.name')|trans }}</span>
</div>
<div>
{% for place in workflow_marked_places(task) %}
<span class="task-status box type-{{ task.type }} place-{{ place }}">{{ place|trans }}</span>
{% endfor %}
{% if task.assignee is not null %}
<div class="chill-task-list__row__assignee">
<span class="chill_task-list__row__assignee_by">{{ 'By'|trans }}&nbsp;:</span>
{{ task.assignee.username }}</div>
{% endif %}
</div>
{% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %}
<div class="chill-task-list__row__dates">
<ul class="record_actions column">
{% if task.startDate is not null %}
<li title="{{ 'Start'|trans|escape('html_attr') }}">
<i class="fa fa-play"></i>
{{ task.startDate|format_date('medium') }}
</li>
{% endif %}
{% if task.warningDate is not null %}
<li title="{{ 'Warning'|trans|escape('html_attr') }}">
<i class="fa fa-exclamation-triangle"></i>
{{ task.warningDate|format_date('medium') }}
</li>
{% endif %}
{% if task.endDate is not null %}
<li title="{{ 'End'|trans|escape('html_attr') }}">
<i class="fa fa-hourglass-end"></i>
{{ task.endDate|format_date('medium') }}
</li>
{% endif %}
</ul>
</div>
{% endif %}
</td>
<td>
<ul class="record_actions">
{% if workflow_transitions(task)|length > 0 %}
<li>
<div class="btn-group">
<a class="btn btn-task-exchange dropdown-toggle" href="#" role="button" id="taskExchange" data-bs-toggle="dropdown" aria-expanded="false">
{{'Change task status'|trans}}
</a>
<ul class="dropdown-menu" aria-labelledby="taskExchange">
{% for transition in workflow_transitions(task) %}
<li>
<a class="dropdown-item" href="{{ path('chill_task_task_transition', { 'taskId': task.id, 'transition': transition.name, 'kind': 'single-task', 'list_params': app.request.query.all }) }}" class="{{ task_workflow_metadata(task, 'transition.class', transition)|e('html_attr') }}">
{{ task_workflow_metadata(task, 'transition.verb', transition)|trans }}
</a>
</li>
{% endfor %}
</ul>
</div>
</li>
{% endif %}
<li>
<a href="{{ path('chill_task_single_task_show', { 'id': task.id, 'list_params': app.request.query.all }) }}" class="btn btn-show "></a>
</li>
{% if is_granted('CHILL_TASK_TASK_UPDATE', task) %}
<li>
<a href="{{ path('chill_task_single_task_edit', { 'id': task.id, 'list_params': app.request.query.all }) }}" class="btn btn-update "></a>
</li>
{% endif %}
{% if is_granted('CHILL_TASK_TASK_DELETE', task) %}
<li>
<a href="{{ path('chill_task_single_task_delete', { 'id': task.id, 'list_params': app.request.query.all } ) }}" class="btn btn-delete "></a>
</li>
{% endif %}
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if isSingleStatus %}
{% if tasks|length < paginator.getTotalItems %}
{{ chill_pagination(paginator) }}
{% endif %}
<!-- lien retour -->
<ul class="record_actions">
<li>
{% if person is not null %}
<a href="{{ path('chill_task_singletask_list', {'person_id': person.id}) }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }}
</a>
{% endif %}
{% if user is not null %}
<a href="{{ path('chill_task_singletask_list') }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }}
</a>
{% endif %}
</li>
<li>
{% if person is not null %}
<a href="{{ path('chill_task_single_task_new', {'person_id': person.id}) }}" class="btn btn-create">
{{ 'Add a new task' | trans }}
</a>
{% endif %}
{% if user is not null %}
<a href="{{ path('chill_task_single_task_new') }}" class="btn btn-create">
{{ 'Add a new task' | trans }}
</a>
{% endif %}
</li>
</ul>
{% else %}
<ul class="record_actions">
<li>
<a href="{{ path('chill_task_singletask_list', app.request.query.all|merge({ 'status': [ status ] })) }}" class="btn btn-misc">
{{ 'See more' | trans }}
</a>
</li>
</ul>
{% endif %}
{% endif %}
{% endmacro %}
{% import _self as helper %}
<h1>{{ app.request.query.get('title', null)|escape('html')|default('Task list'|trans) }}</h1>
{% if false == app.request.query.boolean('hide_form', false) %}
<h2>{{ 'Filter the tasks'|trans }}</h2>
{{ form_start(form) }}
{{ form_row(form.user_id) }}
{% if form.status is defined %}
{{ form_row(form.status) }}
{% endif %}
{% if form.types is defined %}
{{ form_row(form.types) }}
{% endif %}
{% if form.person_id is defined %}
{{ form_row(form.person_id) }}
{% endif %}
{% if form.center_id is defined %}
{{ form_row(form.center_id) }}
{% endif %}
<ul class="record_actions">
<li>
<button type="submit" class="btn btn-submit">{{ 'Filter'|trans }}</button>
</li>
</ul>
{{ form_end(form)}}
{% endif %}
{% if tasks_count == 0 %}
<p class="chill-no-data-statement">{{ "There is no tasks."|trans }}</p>
{% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %}
<ul class="record_actions">
<li>
{% if person is not null %}
<a href="{{ path('chill_task_single_task_new', {'person_id': person.id}) }}" class="btn btn-create">
{{ 'Add a new task' | trans }}
</a>
{% endif %}
</li>
</ul>
{% endif %}
{% else %}
{% if false == app.request.query.boolean('hide_form', false) %}
<h2>{{ 'Tasks'|trans }}</h2>
{% endif %}
{# TODO reimplement right to create task. #}
{# {% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %} #}
<ul class="record_actions">
<li>
{% if person is not null %}
<a href="{{ path('chill_task_single_task_new', {'person_id': person.id}) }}" class="btn btn-create">
{{ 'Add a new task' | trans }}
</a>
{% endif %}
</li>
</ul>
{# {% endif %} #}
{% if single_task_ended_tasks is defined %}
{{ helper.date_status('Tasks with expired deadline', single_task_ended_tasks, single_task_ended_count, single_task_ended_paginator, 'ended', isSingleStatus, person) }}
{% endif %}
{% if single_task_warning_tasks is defined %}
{{ helper.date_status('Tasks with warning deadline reached', single_task_warning_tasks, single_task_warning_count, single_task_warning_paginator, 'warning', isSingleStatus, person) }}
{% endif %}
{% if single_task_current_tasks is defined %}
{{ helper.date_status('Current tasks', single_task_current_tasks, single_task_current_count, single_task_current_paginator, 'current', isSingleStatus, person) }}
{% endif %}
{% if single_task_not_started_tasks is defined %}
{{ helper.date_status('Tasks not started', single_task_not_started_tasks, single_task_not_started_count, single_task_not_started_paginator, 'not_started', isSingleStatus, person) }}
{% endif %}
{% if single_task_closed_tasks is defined %}
{{ helper.date_status('Closed tasks', single_task_closed_tasks, single_task_closed_count, single_task_closed_paginator, 'closed', isSingleStatus, person) }}
{% endif %}
{% if isSingleStatus == false %}
<ul class="record_actions">
<li>
{% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %}
<a href="{{ path('chill_task_single_task_new', {'person_id': person.id}) }}" class="btn btn-create">
{{ 'Add a new task' | trans }}
</a>
{% endif %}
{% if accompanyingCourse is not null %}
<a href="{{ path('chill_task_single_task_new', {'course_id': accompanyingCourse.id}) }}" class="btn btn-create">
{{ 'Add a new task' | trans }}
</a>
{% endif %}
</li>
</ul>
{% endif %}
{% endif %}

View File

@@ -14,37 +14,17 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% extends "@ChillPerson/Person/layout.html.twig" %}
{% extends person is defined ? "@ChillPerson/Person/layout.html.twig" : "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = 'chill_task_single_task_new' %}
{% set person = task.person %}
{% block title %}{{ 'New task'|trans }}{% endblock %}
{% block title %}
{{ 'New task'|trans }}
{% endblock %}
{% block personcontent %}
<div class="task-new">
<h1>{{ 'New task'|trans }}</h1>
{% include '@ChillTask/SingleTask/_new.html.twig' %}
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_row(form.title) }}
{{ form_row(form.description) }}
{{ form_row(form.assignee) }}
{{ form_row(form.circle) }}
{{ form_row(form.startDate) }}
{{ form_row(form.endDate) }}
{{ form_row(form.warningInterval) }}
<ul class="record_actions sticky-form-buttons">
<li>
{{ form_widget(form.submit, { 'label': 'Add a new task'|trans, 'attr': {'class': 'btn btn-save'} }) }}
</li>
</ul>
{{ form_end(form) }}
</div>
{% endblock %}

View File

@@ -0,0 +1,33 @@
{#
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% extends person is defined ? "@ChillPerson/Person/layout.html.twig" : "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
{% set activeRouteKey = 'chill_task_single_task_show' %}
{% set person = task.person %}
{% block title %}
{{ 'Task'|trans }}
{% endblock %}
{% block personcontent %}
{% include '@ChillTask/SingleTask/_show.html.twig' %}
{% endblock %}

View File

@@ -0,0 +1,12 @@
{% extends "@ChillPerson/Person/layout.html.twig" %}
{% set activeRouteKey = 'chill_task_task_list' %}
{% set person = task.person %}
{% block title 'Remove task'|trans %}
{% block personcontent %}
{% include '@ChillTask/SingleTask/_transition.html.twig' %}
{% endblock %}

View File

@@ -0,0 +1,25 @@
<div class="task-edit">
<h1>{{ 'Edit task'|trans }}</h1>
{{ form_start(form) }}
{{ form_row(form.title) }}
{{ form_row(form.description) }}
{{ form_row(form.assignee) }}
{{ form_row(form.circle) }}
{{ form_row(form.startDate) }}
{{ form_row(form.endDate) }}
{{ form_row(form.warningInterval) }}
<ul class="record_actions sticky-form-buttons">
<li class="cancel">
<a class="btn btn-cancel" href={% if task.person is not null %} "{{ path('chill_task_singletask_list', { 'person_id': task.person.id, 'list_params': app.request.query.get('list_params', {} )} ) }}" {% else %} "{{ chill_return_path_or('chill_task_singletask_courselist', {'course_id': task.course.id}) }}" {% endif %}>
{{ 'Cancel'|trans }}</a>
</li>
<li>
{{ form_widget(form.submit, { 'label': 'Save task', 'attr': {'class' : 'btn btn-update'}})}}
</li>
</ul>
{{ form_end(form) }}
</div>

View File

@@ -1,244 +0,0 @@
{% macro date_status(title, tasks, count, paginator, status, isSingleStatus, person, user) %}
{% if tasks|length > 0 %}
<h3>{{ title|trans }}</h3>
<table class="table table-bordered border-dark chill-task-list">
<tbody>
{% for task in tasks %}
<tr>
<td>
<div>
<span class="chill-task-list__row__title">{{ task.title }}</span>
</div>
{% if person is null %}
<div>
<span class="chill-task-list__row__person-for">{{ 'For person'|trans }}&nbsp;:</span> <span class="chill-task-list__row__person"><a href="{{ path('chill_person_view', {person_id : task.person.Id}) }}">{{ task.person}}</a></span>
</div>
{% endif %}
<div>
<span class="chill-task-list__row__type">{{ task_workflow_metadata(task, 'definition.name')|trans }}</span>
</div>
<div>
{% for place in workflow_marked_places(task) %}
<span class="task-status box type-{{ task.type }} place-{{ place }}">{{ place|trans }}</span>
{% endfor %}
{% if task.assignee is not null %}
<div class="chill-task-list__row__assignee"><span class="chill_task-list__row__assignee_by">{{ 'By'|trans }}&nbsp;:</span> {{ task.assignee.username }}</div>
{% endif %}
</div>
{% if task.startDate is not null or task.warningDate is not null or task.endDate is not null %}
<div class="chill-task-list__row__dates">
<ul class="record_actions column">
{% if task.startDate is not null %}
<li title="{{ 'Start'|trans|escape('html_attr') }}">
<i class="fa fa-play" ></i> {{ task.startDate|format_date('medium') }}
</li>
{% endif %}
{% if task.warningDate is not null %}
<li title="{{ 'Warning'|trans|escape('html_attr') }}">
<i class="fa fa-exclamation-triangle"></i> {{ task.warningDate|format_date('medium') }}
</li>
{% endif %}
{% if task.endDate is not null %}
<li title="{{ 'End'|trans|escape('html_attr') }}">
<i class="fa fa-hourglass-end"></i> {{ task.endDate|format_date('medium') }}
</li>
{% endif %}
</ul>
</div>
{% endif %}
</td>
<td>
<ul class="record_actions">
{% if workflow_transitions(task)|length > 0 %}
<li>
<div class="btn-group">
<a class="btn btn-task-exchange dropdown-toggle" href="#" role="button"
id="taskExchange" data-bs-toggle="dropdown" aria-expanded="false">
{{'Change task status'|trans}}
</a>
<ul class="dropdown-menu" aria-labelledby="taskExchange">
{% for transition in workflow_transitions(task) %}
<li>
<a class="dropdown-item"
href="{{ path('chill_task_task_transition', { 'taskId': task.id, 'transition': transition.name, 'kind': 'single-task', 'list_params': app.request.query.all }) }}" class="{{ task_workflow_metadata(task, 'transition.class', transition)|e('html_attr') }}">
{{ task_workflow_metadata(task, 'transition.verb', transition)|trans }}
</a>
</li>
{% endfor %}
</ul>
</div>
</li>
{% endif %}
<li>
<a href="{{ path('chill_task_single_task_show', { 'id': task.id, 'list_params': app.request.query.all }) }}" class="btn btn-show "></a>
</li>
{% if is_granted('CHILL_TASK_TASK_UPDATE', task) %}
<li>
<a href="{{ path('chill_task_single_task_edit', { 'id': task.id, 'list_params': app.request.query.all }) }}" class="btn btn-update "></a>
</li>
{% endif %}
{% if is_granted('CHILL_TASK_TASK_DELETE', task) %}
<li>
<a href="{{ path('chill_task_single_task_delete', { 'id': task.id, 'list_params': app.request.query.all } ) }}" class="btn btn-delete "></a>
</li>
{% endif %}
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if isSingleStatus %}
{% if tasks|length < paginator.getTotalItems %}
{{ chill_pagination(paginator) }}
{% endif %}
<!-- lien retour -->
<ul class="record_actions">
<li>
{% if person is not null %}
<a href="{{ path('chill_task_singletask_list', {'person_id': person.id}) }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }}
</a>
{% endif %}
{% if user is not null %}
<a href="{{ path('chill_task_singletask_list') }}" class="btn btn-cancel">
{{ 'Back to the list' | trans }}
</a>
{% endif %}
</li>
<li>
{% if person is not null %}
<a href="{{ path('chill_task_single_task_new', {'person_id': person.id}) }}" class="btn btn-create">
{{ 'Add a new task' | trans }}
</a>
{% endif %}
{% if user is not null %}
<a href="{{ path('chill_task_single_task_new') }}" class="btn btn-create">
{{ 'Add a new task' | trans }}
</a>
{% endif %}
</li>
</ul>
{% else %}
<ul class="record_actions">
<li>
<a href="{{ path('chill_task_singletask_list', app.request.query.all|merge({ 'status': [ status ] })) }}" class="btn btn-misc">
{{ 'See more' | trans }}
</a>
</li>
</ul>
{% endif %}
{% endif %}
{% endmacro %}
{% import _self as helper %}
<h1>{{ app.request.query.get('title', null)|escape('html')|default('Task list'|trans) }}</h1>
{% if false == app.request.query.boolean('hide_form', false) %}
<h2>{{ 'Filter the tasks'|trans }}</h2>
{{ form_start(form) }}
{{ form_row(form.user_id) }}
{% if form.status is defined %}
{{ form_row(form.status) }}
{% endif %}
{% if form.types is defined %}
{{ form_row(form.types) }}
{% endif %}
{% if form.person_id is defined %}
{{ form_row(form.person_id) }}
{% endif %}
{% if form.center_id is defined %}
{{ form_row(form.center_id) }}
{% endif %}
<ul class="record_actions">
<li>
<button type="submit" class="btn btn-submit">{{ 'Filter'|trans }}</button>
</li>
</ul>
{{ form_end(form)}}
{% endif %}
{% if tasks_count == 0 %}
<p class="chill-no-data-statement">{{ "There is no tasks."|trans }}</p>
{% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %}
<ul class="record_actions">
<li>
{% if person is not null %}
<a href="{{ path('chill_task_single_task_new', {'person_id': person.id}) }}" class="btn btn-create">
{{ 'Add a new task' | trans }}
</a>
{% endif %}
</li>
</ul>
{% endif %}
{% else %}
{% if false == app.request.query.boolean('hide_form', false) %}
<h2>{{ 'Tasks'|trans }}</h2>
{% endif %}
{% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %}
<ul class="record_actions">
<li>
{% if person is not null %}
<a href="{{ path('chill_task_single_task_new', {'person_id': person.id}) }}" class="btn btn-create">
{{ 'Add a new task' | trans }}
</a>
{% endif %}
</li>
</ul>
{% endif %}
{% if single_task_ended_tasks is defined %}
{{ helper.date_status('Tasks with expired deadline', single_task_ended_tasks, single_task_ended_count, single_task_ended_paginator, 'ended', isSingleStatus, person) }}
{% endif %}
{% if single_task_warning_tasks is defined %}
{{ helper.date_status('Tasks with warning deadline reached', single_task_warning_tasks, single_task_warning_count, single_task_warning_paginator, 'warning', isSingleStatus, person) }}
{% endif %}
{% if single_task_current_tasks is defined %}
{{ helper.date_status('Current tasks', single_task_current_tasks, single_task_current_count, single_task_current_paginator, 'current', isSingleStatus, person) }}
{% endif %}
{% if single_task_not_started_tasks is defined %}
{{ helper.date_status('Tasks not started', single_task_not_started_tasks, single_task_not_started_count, single_task_not_started_paginator, 'not_started', isSingleStatus, person) }}
{% endif %}
{% if single_task_closed_tasks is defined %}
{{ helper.date_status('Closed tasks', single_task_closed_tasks, single_task_closed_count, single_task_closed_paginator, 'closed', isSingleStatus, person) }}
{% endif %}
{% if isSingleStatus == false %}
<ul class="record_actions">
<li>
{% if person is not null and is_granted('CHILL_TASK_TASK_CREATE', person) %}
<a href="{{ path('chill_task_single_task_new', {'person_id': person.id}) }}" class="btn btn-create">
{{ 'Add a new task' | trans }}
</a>
{% endif %}
</li>
</ul>
{% endif %}
{% endif %}

View File

@@ -0,0 +1,30 @@
<div class="task-new">
<h1>{{ 'New task'|trans }}</h1>
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_row(form.title) }}
{{ form_row(form.description) }}
{{ form_row(form.assignee) }}
{{ form_row(form.circle) }}
{{ form_row(form.startDate) }}
{{ form_row(form.endDate) }}
{{ form_row(form.warningInterval) }}
<ul class="record_actions sticky-form-buttons">
<li class="cancel">
<a class="btn btn-cancel" href={% if task.person is not null %} "{{ path('chill_task_singletask_list', { 'person_id': task.person.id, 'list_params': app.request.query.get('list_params', {} )} ) }}" {% else %} "{{ chill_return_path_or('chill_task_singletask_courselist', {'course_id': task.course.id}) }}" {% endif %}>
{{'Cancel'|trans}}
</a>
</li>
<li>
{{ form_widget(form.submit, { 'label': 'Add a new task'|trans, 'attr': {'class': 'btn btn-save'} }) }}
</li>
</ul>
{{ form_end(form) }}
</div>

View File

@@ -0,0 +1,112 @@
<div class="task-show">
<h1>{{ 'Task'|trans }}</h1>
<h2>{{ task.title }}
{% for place in workflow_marked_places(task) %}
<span class="task-status box type-{{ task.type }} place-{{ place }}">{{ place|trans }}</span>
{% endfor %}
</h2>
<dl class="chill_view_data">
<dt class="inline">{{ 'Description'|trans }}</dt>
<dd>
{% if task.description is empty %}
<span class="chill-no-data-statement">{{"No description"|trans}}</span>
{% else %}
<blockquote class="chill-user-quote">
{{ task.description|chill_markdown_to_html }}
</blockquote>
{% endif %}
</dd>
<dt class="inline">{{ 'Assignee'|trans }}</dt>
<dd>
{% if task.assignee is null %}
<span class="chill-no-data-statement">{{"No one assignee"|trans}}</span>
{% else %}
{{ task.assignee }}
{% endif %}
</dd>
{% if task.scope is not null %}
<dt class="inline">{{ 'Scope'|trans }}</dt>
<dd>
<span class="scope">{{ task.scope.name|localize_translatable_string }}</span>
</dd>
{% endif %}
<h3>{{"Dates"|trans}}</h3>
{% if task.startDate is null and task.endDate is null and task.warningDate is null %}
<dt></dt>
<dd>
<span class="chill-no-data-statement">{{"No dates specified"|trans}}</span>
</dd>
</dt>
{% else %}
{% if task.startDate is not null %}
<dt class="inline">{{ 'Start'|trans }}</dt>
<dd>{{ task.startDate|format_date('long') }}</dd>
{% endif %}
{% if task.endDate is not null %}
<dt class="inline">{{ 'End'|trans }}</dt>
<dd>{{ task.endDate|format_date('long') }}</dd>
{% endif %}
{% if task.warningDate is not null %}
<dt class="inline">{{ 'Warning'|trans }}</dt>
<dd>{{ task.warningDate|format_date('long') }}</dd>
{% endif %}
{% endif %}
</dl>
{% if timeline is not null %}
<h3>{{"Timeline"|trans}}</h3>
{{ timeline|raw }}
{% endif %}
<ul class="record_actions sticky-form-buttons">
<li class="cancel">
<a class="btn btn-cancel" href={% if task.person is not null %} "{{ path('chill_task_singletask_list', { 'person_id': task.person.id, 'list_params': app.request.query.get('list_params', {} )} ) }}" {% else %} "{{ chill_return_path_or('chill_task_singletask_courselist', {'course_id': task.course.id}) }}" {% endif %}>
{{'Back to the list'|trans}}
</a>
</li>
{% if workflow_transitions(task)|length > 0 %}
<li>
<div class="btn-group">
<a class="btn btn-task-exchange dropdown-toggle" href="#" role="button" id="taskExchange" data-bs-toggle="dropdown" aria-expanded="false">
{{'Change task status'|trans}}
</a>
<ul class="dropdown-menu" aria-labelledby="taskExchange">
{% for transition in workflow_transitions(task) %}
<li>
<a class="dropdown-item" href="{{ path('chill_task_task_transition', { 'taskId': task.id, 'transition': transition.name, 'kind': 'single-task', 'return_path': app.request.uri }) }}" class="{{ task_workflow_metadata(task, 'transition.class', transition)|e('html_attr') }}">
{{ task_workflow_metadata(task, 'transition.verb', transition)|trans }}
</a>
</li>
{% endfor %}
</ul>
</div>
</li>
{% endif %}
{% if is_granted('CHILL_TASK_TASK_UPDATE', task) %}
<li>
<a class="btn btn-update" href="{{ path('chill_task_single_task_edit', { 'id': task.id, 'list_params': app.request.query.all['list_params'] }) }}">
{{ 'Edit the task'|trans }}
</a>
</li>
{% endif %}
{% if is_granted('CHILL_TASK_TASK_CREATE', task) %}
<li>
<a href="{{ path('chill_task_single_task_delete', { 'id': task.id, 'list_params': app.request.query.all['list_params'] } ) }}" class="btn btn-delete">
{{ 'Delete'|trans }}
</a>
</li>
{% endif %}
</ul></div>

View File

@@ -0,0 +1,23 @@
<h2>{{ 'Apply transition on task <em>%title%</em>'|trans({ '%title%': task.title } )|raw }}</h2>
{% if task_workflow_metadata(task, 'transition.sentence_confirmation', transition) is not empty %}
<p class="message-confirm">{{ task_workflow_metadata(task, 'transition.sentence_confirmation', transition)|trans }}</p>
{% else %}
<p>{{ 'Are you sure to apply the transition %name% on this task ?'|trans({ '%name%': task_workflow_metadata(task, 'transition.name', transition)|default(transition.name)|trans }) }}</p>
{% endif %}
{{ form_start(form) }}
<ul class="record_actions">
<li class="cancel">
<a href="{{ path('chill_task_singletask_list', app.request.query.get('list_params', { }) ) }}" class="btn btn-cancel">
{{ 'Back to the list'|trans }}
</a>
</li>
<li>
{{ form_widget(form.submit, { 'attr' : { 'class' : "btn btn-task-exchange green" }, 'label': task_workflow_metadata(task, 'transition.apply_transition_submit_label', transition)|default('apply')|trans } ) }}
</li>
</ul>
{{ form_end(form) }}

View File

@@ -1,59 +0,0 @@
{#
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% extends "@ChillPerson/Person/layout.html.twig" %}
{% set activeRouteKey = 'chill_task_single_task_edit' %}
{% set person = task.person %}
{% block title %}{{ 'Edit task'|trans }}{% endblock %}
{% block personcontent %}
<div class="task-edit">
<h1>{{ 'Edit task'|trans }}</h1>
{{ form_start(form) }}
{{ form_row(form.title) }}
{{ form_row(form.description) }}
{{ form_row(form.assignee) }}
{{ form_row(form.circle) }}
{{ form_row(form.startDate) }}
{{ form_row(form.endDate) }}
{{ form_row(form.warningInterval) }}
<ul class="record_actions sticky-form-buttons">
<li class="cancel">
<a class="btn btn-cancel" href="{% apply spaceless %}
{% if app.request.query.has('returnPath') %}
{{ app.request.query.get('returnPath')|escape('html_attr') }}">
{% else %}
{{ path('chill_task_single_task_show', { 'id': task.id, 'list_params': app.request.query.get('list_params', { } )} ) }}
{% endif %}
{% endapply %}">
{{ app.request.query.get('returnLabel')|default('Cancel'|trans) }}
</a>
</li>
<li>
{{ form_widget(form.submit, { 'label': 'Save task', 'attr': {'class' : 'btn btn-update'}})}}
</li>
</ul>
{{ form_end(form) }}
</div>
{% endblock %}

View File

@@ -15,30 +15,32 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% extends '@ChillPerson/Person/layout.html.twig' %}
{% extends layout %}
{% set activeRouteKey = 'chill_task_single_task_new' %}
{% block title %}{{ 'Task list'|trans }}{% endblock %}
{% block title %}
{{ 'Task list'|trans }}
{% endblock %}
{% macro thead() %}
{% endmacro %}
{% macro thead() %}{% endmacro %}
{% macro row(task) %}
{% endmacro %}
{% macro row(task) %}{% endmacro %}
{% block filtertasks %}
{% if person is not null %}
{% block personcontent %}
<div class="tasks">
{% include 'ChillTaskBundle:SingleTask:_list.html.twig' %}
</div>
{% endblock %}
{% else %}
{% block content %}
<div class="col-md-10 col-xxl tasks">
{% include 'ChillTaskBundle:SingleTask:_list.html.twig' %}
</div>
{% endblock %}
{% endif %}
{% if person is not null %}
{% block personcontent %}
<div class="tasks">
{% include '@ChillTask/SingleTask/Person/list.html.twig' %}
</div>
{% endblock %}
{% else %}
{% block content %}
<div class="col-md-10 col-xxl tasks">
{% include '@ChillTask/SingleTask/AccompanyingCourse/list.html.twig' %}
</div>
{% endblock %}
{% endif %}
{% endblock %}

View File

@@ -1,139 +0,0 @@
{#
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% extends "@ChillPerson/Person/layout.html.twig" %}
{% set activeRouteKey = 'chill_task_single_task_show' %}
{% set person = task.person %}
{% block title %}{{ 'Task'|trans }}{% endblock %}
{% block personcontent %}
<div class="task-show">
<h1>{{ 'Task'|trans }}</h1>
<h2>{{ task.title }} {% for place in workflow_marked_places(task) %}
<span class="task-status box type-{{ task.type }} place-{{ place }}">{{ place|trans }}</span>
{% endfor %}</h2>
<dl class="chill_view_data">
<dt class="inline">{{ 'Description'|trans }}</dt>
<dd>
{% if task.description is empty %}
<span class="chill-no-data-statement">{{"No description"|trans}}</span>
{% else %}
<blockquote class="chill-user-quote">
{{ task.description|chill_markdown_to_html }}
</blockquote>
{% endif %}
</dd>
<dt class="inline">{{ 'Assignee'|trans }}</dt>
<dd>
{% if task.assignee is null %}
<span class="chill-no-data-statement">{{"No one assignee"|trans}}</span>
{% else %}
{{ task.assignee }}
{% endif %}
</dd>
<dt class="inline">{{ 'Scope'|trans }}</dt>
<dd><span class="scope">{{ task.scope.name|localize_translatable_string }}</span></dd>
<h3>{{"Dates"|trans}}</h3>
{% if task.startDate is null and task.endDate is null and task.warningDate is null %}
<dt>
<dd><span class="chill-no-data-statement">{{"No dates specified"|trans}}</span></dd>
</dt>
{% else %}
{% if task.startDate is not null %}
<dt class="inline">{{ 'Start'|trans }}</dt>
<dd>{{ task.startDate|format_date('long') }}</dd>
{% endif %}
{% if task.endDate is not null %}
<dt class="inline">{{ 'End'|trans }}</dt>
<dd>{{ task.endDate|format_date('long') }}</dd>
{% endif %}
{% if task.warningDate is not null %}
<dt class="inline">{{ 'Warning'|trans }}</dt>
<dd>{{ task.warningDate|format_date('long') }}</dd>
{% endif %}
{% endif %}
</dl>
{% if timeline is not null %}
<h3>{{"Timeline"|trans}}</h3>
{{ timeline|raw }}
{% endif %}
<ul class="record_actions sticky-form-buttons">
<li class="cancel">
<a class="btn btn-cancel" href="{%- if app.request.query.has('returnPath') -%}
{{ app.request.query.get('returnPath')|escape('html_attr') }}
{%- else -%}
{{ path('chill_task_singletask_list', app.request.query.get('list_params', {}) ) }}
{%- endif -%}">
{{ app.request.query.get('returnLabel')|default('Back to the list'|trans) }}
</a>
</li>
{% if workflow_transitions(task)|length > 0 %}
<li>
<div class="btn-group">
<a class="btn btn-task-exchange dropdown-toggle" href="#" role="button"
id="taskExchange" data-bs-toggle="dropdown" aria-expanded="false">
{{'Change task status'|trans}}
</a>
<ul class="dropdown-menu" aria-labelledby="taskExchange">
{% for transition in workflow_transitions(task) %}
<li>
<a class="dropdown-item"
href="{{ path('chill_task_task_transition', { 'taskId': task.id, 'transition': transition.name, 'kind': 'single-task', 'return_path': app.request.uri }) }}" class="{{ task_workflow_metadata(task, 'transition.class', transition)|e('html_attr') }}">
{{ task_workflow_metadata(task, 'transition.verb', transition)|trans }}
</a>
</li>
{% endfor %}
</ul>
</div>
</li>
{% endif %}
{% if is_granted('CHILL_TASK_TASK_UPDATE', task) %}
<li>
<a class="btn btn-update" href="{{ path('chill_task_single_task_edit', { 'id': task.id }) }}">
{{ 'Edit the task'|trans }}
</a>
</li>
{% endif %}
{% if is_granted('CHILL_TASK_TASK_CREATE', task) %}
<li>
<a href="{{ path('chill_task_single_task_delete', { 'id': task.id } ) }}" class="btn btn-delete">
{{ 'Delete'|trans }}
</a>
</li>
{% endif %}
</ul>
</div>
{% endblock %}

View File

@@ -1,34 +0,0 @@
{% extends "@ChillPerson/Person/layout.html.twig" %}
{% set activeRouteKey = 'chill_task_task_list' %}
{% set person = task.person %}
{% block title 'Remove task'|trans %}
{% block personcontent %}
<h2>{{ 'Apply transition on task <em>%title%</em>'|trans({ '%title%': task.title } )|raw }}</h2>
{% if task_workflow_metadata(task, 'transition.sentence_confirmation', transition) is not empty %}
<p class="message-confirm">{{ task_workflow_metadata(task, 'transition.sentence_confirmation', transition)|trans }}</p>
{% else %}
<p>{{ 'Are you sure to apply the transition %name% on this task ?'|trans({ '%name%': task_workflow_metadata(task, 'transition.name', transition)|default(transition.name)|trans }) }}</p>
{% endif %}
{{ form_start(form) }}
<ul class="record_actions">
<li class="cancel">
<a href="{{ path('chill_task_singletask_list', app.request.query.get('list_params', { }) ) }}" class="btn btn-cancel">
{{ 'Back to the list'|trans }}
</a>
</li>
<li>
{{ form_widget(form.submit, { 'attr' : { 'class' : "btn btn-task-exchange green" }, 'label': task_workflow_metadata(task, 'transition.apply_transition_submit_label', transition)|default('apply')|trans } ) }}
</li>
</ul>
{{ form_end(form) }}
{% endblock %}

View File

@@ -32,7 +32,9 @@ 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 Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Chill\TaskBundle\Security\Authorization\AuthorizationEvent;
@@ -144,17 +146,15 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy
// do regular check.
return $this->voter->voteOnAttribute($attribute, $subject, $token);
/*
if ($subject instanceof AbstractTask) {
if ($subject->getPerson() === null) {
$associated = $subject->getPerson() ?? $subject->getCourse();
if ($associated === null) {
throw new \LogicException("You should associate a person with task "
. "in order to check autorizations");
}
$person = $subject->getPerson();
} elseif ($subject instanceof Person) {
$person = $subject;
} else {
// subject is null. We check that at least one center is reachable
$centers = $this->authorizationHelper->getReachableCenters($token->getUser(), new Role($attribute));
@@ -168,6 +168,10 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy
if (NULL === $center) {
return false;
} elseif ($associated instanceof AccompanyingPeriod && !$this->accessDecisionManager->decide($token, [AccompanyingPeriodVoter::SEE], $associated)) {
return false;
} elseif ($associated instanceof AccompanyingPeriod && !$this->accessDecisionManager->decide($token, [AccompanyingPeriodVoter::SEE], $associated)) {
return false;
}
return $this->authorizationHelper->userHasAccess(
@@ -175,7 +179,7 @@ final class TaskVoter extends AbstractChillVoter implements ProvideRoleHierarchy
$subject,
$attribute
);
*/
}
public function getRoles()

View File

@@ -1,11 +1,6 @@
services:
Chill\TaskBundle\Controller\:
resource: '../../Controller'
tags: ['controller.service_arguments']
Chill\TaskBundle\Controller\SingleTaskController:
arguments:
$eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface'
$timelineBuilder: '@chill_main.timeline_builder'
$logger: '@chill.main.logger'
tags: ['controller.service_arguments']
Chill\TaskBundle\Controller\:
resource: "../../Controller"
autowire: true
autoconfigure: true
tags: ["controller.service_arguments"]

View File

@@ -1,23 +1,22 @@
services:
Chill\TaskBundle\Menu\UserMenuBuilder:
arguments:
$tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'
$counter: '@Chill\TaskBundle\Templating\UI\CountNotificationTask'
$translator: '@Symfony\Component\Translation\TranslatorInterface'
$authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'
tags:
- { name: 'chill.menu_builder' }
Chill\TaskBundle\Menu\PersonMenuBuilder:
arguments:
$authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'
$translator: '@Symfony\Component\Translation\TranslatorInterface'
tags:
- { name: 'chill.menu_builder' }
Chill\TaskBundle\Menu\SectionMenuBuilder:
arguments:
$authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'
$translator: '@Symfony\Component\Translation\TranslatorInterface'
tags:
- { name: 'chill.menu_builder' }
Chill\TaskBundle\Menu\UserMenuBuilder:
arguments:
$tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'
$counter: '@Chill\TaskBundle\Templating\UI\CountNotificationTask'
$translator: '@Symfony\Component\Translation\TranslatorInterface'
$authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'
tags:
- { name: "chill.menu_builder" }
Chill\TaskBundle\Menu\MenuBuilder:
autowire: true
autoconfigure: true
tags:
- { name: "chill.menu_builder" }
Chill\TaskBundle\Menu\SectionMenuBuilder:
arguments:
$authorizationChecker: '@Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface'
$translator: '@Symfony\Component\Translation\TranslatorInterface'
tags:
- { name: "chill.menu_builder" }

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Task;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20210909153533 extends AbstractMigration
{
public function getDescription(): string
{
return 'An accompanying period can be linked to a task. Key course_id is added to single task entity';
}
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');
$this->addSql('ALTER TABLE chill_task.recurring_task DROP course_id');
$this->addSql('ALTER TABLE chill_task.single_task DROP CONSTRAINT FK_194CB3D8591CC992');
$this->addSql('ALTER TABLE chill_task.single_task DROP course_id');
}
}

View File

@@ -1,53 +1,55 @@
Tasks: 'Tâches'
'New task': 'Nouvelle tâche'
'Add a new task': 'Ajouter une nouvelle tâche'
Tasks: "Tâches"
"New task": "Nouvelle tâche"
"Add a new task": "Ajouter une nouvelle tâche"
Title: Titre
Description: Description
Assignee: 'Personne assignée'
Assignee: "Personne assignée"
Scope: Cercle
'Start date': 'Date de début'
'End date': "Date d'échéance"
'Warning date': "Date d'avertissement"
'Warning interval': "Délai d'avertissement avant la date d'échéance"
'Unknown dates': 'Dates non spécifiées'
'N': ''
'Unit': ''
"Start date": "Date de début"
"End date": "Date d'échéance"
"Warning date": "Date d'avertissement"
"Warning interval": "Délai d'avertissement avant la date d'échéance"
"Unknown dates": "Dates non spécifiées"
"N": ""
"Unit": ""
Task: Tâche
Details: Détails
Person: Personne
Date: Date
Dates: Dates
User: Utilisateur
'Task list': 'Liste des tâches'
'Tasks with expired deadline': "Tâches avec une date d'échéance dépassée"
'Tasks with warning deadline reached': "Tâches avec une date d'avertissement atteinte"
'Current tasks': 'Tâches en cours'
'Closed tasks': Tâches terminées
'Tasks not started': 'Tâches non commencées'
'Task start date': 'Date de début'
'Task warning date': "Date d'avertissement"
'Task end date': "Date d'échéance"
'Start': 'Début'
'Warning': 'Avertissement'
'End': 'Échéance'
'Task type': 'Type'
'Task status': 'Statut'
'Edit the task': 'Modifier la tâche'
'Edit task': 'Modifier la tâche'
'Save task': 'Enregistrer la tâche'
'View the task': 'Voir la tâche'
'Update the task': 'Mettre à jour la tâche'
'Remove task': 'Supprimer la tâche'
'Delete': 'Supprimer'
'Change task status': 'Changer le statut'
"Task list": "Liste des tâches"
"Tasks with expired deadline": "Tâches avec une date d'échéance dépassée"
"Tasks with warning deadline reached": "Tâches avec une date d'avertissement atteinte"
"Current tasks": "Tâches en cours"
"Closed tasks": Tâches terminées
"Tasks not started": "Tâches non commencées"
"Task start date": "Date de début"
"Task warning date": "Date d'avertissement"
"Task end date": "Date d'échéance"
"Start": "Début"
"Warning": "Avertissement"
"End": "Échéance"
"Task type": "Type"
"Task status": "Statut"
"Edit the task": "Modifier la tâche"
"Edit task": "Modifier la tâche"
"Save task": "Enregistrer la tâche"
"View the task": "Voir la tâche"
"Update the task": "Mettre à jour la tâche"
"Remove task": "Supprimer la tâche"
"Delete": "Supprimer"
"Change task status": "Changer le statut"
'Are you sure you want to remove the task about "%name%" ?': 'Êtes-vous sûr·e de vouloir supprimer la tâche de "%name%"?'
'See more': 'Voir plus'
'Associated tasks': 'Tâches associées'
'My tasks': 'Mes tâches'
'No description': 'Pas de description'
'No dates specified': 'Dates non spécifiées'
'No one assignee': 'Aucune personne assignée'
'Task types': Types de tâches
'Are you sure you want to remove the task about accompanying period "%id%" ?': 'Êtes-vous sûr·e de vouloir supprimer la tâche du parcours "%id%"?'
"See more": "Voir plus"
"Associated tasks": "Tâches associées"
"My tasks": "Mes tâches"
"Tasks for this accompanying period": "Tâches pour ce parcours d'accompagnement"
"No description": "Pas de description"
"No dates specified": "Dates non spécifiées"
"No one assignee": "Aucune personne assignée"
"Task types": Types de tâches
Days: Jour(s)
Weeks: Semaine(s)
Months: Mois
@@ -63,36 +65,36 @@ For person: Pour
By: Par
# transitions - default task definition
'new': 'nouvelle'
'in_progress': 'en cours'
'closed': 'fermée'
'canceled': 'supprimée'
"new": "nouvelle"
"in_progress": "en cours"
"closed": "fermée"
"canceled": "supprimée"
start: démarrer
close: clotûrer
cancel: annuler
Start_verb: Démarrer
Close_verb: Clotûrer
Set this task to cancel state: Marquer cette tâche comme annulée
'%user% has closed the task': '%user% a fermé la tâche'
'%user% has canceled the task': '%user% a annulé la tâche'
'%user% has started the task': '%user% a commencé la tâche'
'%user% has created the task': '%user% a introduit la tâche'
"%user% has closed the task": "%user% a fermé la tâche"
"%user% has canceled the task": "%user% a annulé la tâche"
"%user% has started the task": "%user% a commencé la tâche"
"%user% has created the task": "%user% a introduit la tâche"
Are you sure you want to close this task ?: Êtes-vous sûrs de vouloir clotûrer cette tâche ?
Are you sure you want to cancel this task ?: Êtes-vous sûrs de vouloir annuler cette tâche ?
Are you sure you want to start this task ?: Êtes-vous sûrs de vouloir démarrer cette tâche ?
#Flash messages
'The task is created': 'La tâche a été créée'
'There is no tasks.': Aucune tâche.
'The task has been successfully removed.': 'La tâche a bien été supprimée'
'This form contains errors': 'Ce formulaire contient des erreurs'
'The task has been updated': 'La tâche a été mise à jour'
'The transition is successfully applied': 'La transition a bien été effectuée'
'The transition could not be applied': "La transition n'a pas pu être appliquée"
"The task is created": "La tâche a été créée"
"There is no tasks.": Aucune tâche.
"The task has been successfully removed.": "La tâche a bien été supprimée"
"This form contains errors": "Ce formulaire contient des erreurs"
"The task has been updated": "La tâche a été mise à jour"
"The transition is successfully applied": "La transition a bien été effectuée"
"The transition could not be applied": "La transition n'a pas pu être appliquée"
#widget
'%number% tasks over deadline': '{0} Aucune tâche dépassée|{1} Une tâche dépassée | ]1,Inf[ %count% tâches dépassées'
'%number% tasks near deadline': '{0} Aucune tâche en rappel|{1} Une tâche en rappel | ]1,Inf[ %count% tâches en rappel'
"%number% tasks over deadline": "{0} Aucune tâche dépassée|{1} Une tâche dépassée | ]1,Inf[ %count% tâches dépassées"
"%number% tasks near deadline": "{0} Aucune tâche en rappel|{1} Une tâche en rappel | ]1,Inf[ %count% tâches en rappel"
#title
My tasks near deadline: Mes tâches à échéance proche
@@ -107,4 +109,4 @@ All centers: Tous les centres
CHILL_TASK_TASK_CREATE: Ajouter une tâche
CHILL_TASK_TASK_DELETE: Supprimer une tâche
CHILL_TASK_TASK_SHOW: Voir une tâche
CHILL_TASK_TASK_UPDATE: Modifier une tâche
CHILL_TASK_TASK_UPDATE: Modifier une tâche