diff --git a/Controller/TaskController.php b/Controller/TaskController.php index decb68944..91094c480 100644 --- a/Controller/TaskController.php +++ b/Controller/TaskController.php @@ -9,29 +9,68 @@ use Chill\PersonBundle\Entity\Person; use Chill\TaskBundle\Entity\SingleTask; use Chill\MainBundle\Entity\User; use Chill\PersonBundle\Security\Authorization\PersonVoter; +use Chill\TaskBundle\Repository\SingleTaskRepository; +use Symfony\Component\HttpFoundation\Response; class TaskController extends Controller { /** - * @Route("/{_locale}/task/task/list/{personId}") + * @Route( + * "/{_locale}/task/task/list/{personId}", + * name="chill_task_task_list" + * ) */ public function listAction(Request $request, Person $personId) { $person = $personId; - $em = $this->getDoctrine() - ->getManager(); - // collect parameters for filter - $params = []; + /* @var $taskRepository SingleTaskRepository */ + $taskRepository = $this->get('chill_task.single_task_repository'); + /* @var $paginatorFactory \Chill\MainBundle\Pagination\PaginatorFactory */ + $paginatorFactory = $this->get('chill_main.paginator_factory'); + /* @var $viewParams array The parameters for the view */ + $viewParams['person'] = $person; + // collect parameters for filter $params['person'] = $person; - $singleTasks = $this->get('chill_task.single_task_repository') - ->findByParameters($params, $this->getUser()); + if ($request->query->has('date_status')) { + $statuses = $request->query->get('date_status'); + $singleStatus = count($statuses) === 1; + // check for invalid parameters + $diff = \array_diff( + $statuses, + SingleTaskRepository::DATE_STATUSES) + ; + + if (count($diff) > 0) { + return new Response( + 'date_status not allowed: '. \implode(', ', $diff), + Response::HTTP_BAD_REQUEST + ); + } + } + + foreach(SingleTaskRepository::DATE_STATUSES as $type) { + if($request->query->has('date_status') + && FALSE === \in_array($type, $statuses ?? [])) { + continue; + } + + $params['date_status'] = $type; + $count = $taskRepository + ->countByParameters($params, $this->getUser()) + ; + $paginator = $paginatorFactory->create($count); + $viewParams['single_task_'.$type.'_count'] = $count; + $viewParams['single_task_'.$type.'_paginator'] = $paginator; + $viewParams['single_task_'.$type.'_tasks'] = $taskRepository + ->findByParameters($params, $this->getUser(), + $singleStatus ? $paginator->getCurrentPage()->getFirstItemNumber() : 0, + $singleStatus ? $paginator->getItemsPerPage() : 10) + ; + } - return $this->render('ChillTaskBundle:Task:index.html.twig', [ - 'single_tasks' => $singleTasks, - 'person' => $person - ]); + return $this->render('ChillTaskBundle:Task:index.html.twig', $viewParams); } protected function getPersonParam(Request $request, EntityManagerInterface $em) diff --git a/Repository/SingleTaskRepository.php b/Repository/SingleTaskRepository.php index 26583bd8f..67a26fcf6 100644 --- a/Repository/SingleTaskRepository.php +++ b/Repository/SingleTaskRepository.php @@ -7,12 +7,26 @@ use Doctrine\ORM\QueryBuilder; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Symfony\Component\Security\Core\Role\Role; use Chill\TaskBundle\Security\Authorization\TaskVoter; +use Doctrine\DBAL\Types\Type; /** * SingleTaskRepository */ class SingleTaskRepository extends \Doctrine\ORM\EntityRepository { + + const DATE_STATUS_ENDED = 'ended'; + const DATE_STATUS_WARNING = 'warning'; + const DATE_STATUS_CURRENT = 'current'; + const DATE_STATUS_NOT_STARTED = 'not_started'; + + const DATE_STATUSES = [ + self::DATE_STATUS_ENDED, + self::DATE_STATUS_WARNING, + self::DATE_STATUS_CURRENT, + self::DATE_STATUS_NOT_STARTED + ]; + /** * * @var AuthorizationHelper @@ -24,6 +38,42 @@ class SingleTaskRepository extends \Doctrine\ORM\EntityRepository $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) + { + $qb = $this->createQueryBuilder('st') + ->select('COUNT(st)'); + + $this->buildQuery($qb, $params, $currentUser); + + return (int) $qb + ->getQuery() + ->getSingleScalarResult() + ; + } + + /** + * Find task for given parameters. + * + * Available parameters: + * + * - `person` : filter by person associated with the task ; + * - `date_status`: type of task. To choose between : + * `ended`, `warning`, `current`, `not_started` + * + * @param type $params + * @param User $currentUser + * @return type + */ public function findByParameters($params, User $currentUser) { $qb = $this->createQueryBuilder('st'); @@ -44,8 +94,110 @@ class SingleTaskRepository extends \Doctrine\ORM\EntityRepository $qb->andWhere($qb->expr()->eq('st.person', ':person')); $qb->setParameter('person', $params['person']); } + + if (\array_key_exists('date_status', $params)) { + $this->addTypeFilter($qb, $params); + } } + protected function addTypeFilter(QueryBuilder $qb, $params) + { + $andWhere = $qb->expr()->andX(); + + switch ($params['date_status']) { + case self::DATE_STATUS_ENDED: + $andWhere->add($this->buildNowIsAfterEndDate($qb)); + break; + + case self::DATE_STATUS_WARNING: + $andWhere + ->add($this->buildNowIsAfterEndDate($qb, true)) + ->add($this->buildNowIsAfterWarningDate($qb)) + ; + break; + + case self::DATE_STATUS_CURRENT: + // st.endDate is NULL or (st.endDate is not null and st.endDate < now)) + $andWhere + ->add($this->buildNowIsAfterEndDate($qb, true)) + ->add($this->buildNowIsAfterWarningDate($qb, true)) + ->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)) + ; + } + $qb->setParameter('now', new \DateTime('today'), Type::DATE); + $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()->lt('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, $negative = false) + { + if ($negative === false) { + return $qb->expr()->andX() + ->add($qb->expr()->lt( + $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, $negative = false) + { + if ($negative === false) { + return $qb->expr()->orX() + ->add($qb->expr()->lt('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')) + ; + } + } + + protected function buildACLQuery(QueryBuilder $qb, User $currentUser) { if (NULL === $this->authorizationHelper) { diff --git a/Resources/views/Task/index.html.twig b/Resources/views/Task/index.html.twig index c5de9e688..e379ea44d 100644 --- a/Resources/views/Task/index.html.twig +++ b/Resources/views/Task/index.html.twig @@ -21,85 +21,70 @@ {% block title %}{{ 'Task list'|trans }}{% endblock %} {% macro thead() %} - - - {{ 'Title'|trans }} - {{ 'Task type'|trans }} - {{ 'Task status'|trans }} - {{ 'Task start date'|trans }} - {{ 'Task warning date'|trans }} - {{ 'Task end date'|trans }} - - + {% endmacro %} {% macro row(task) %} - - {{ task.title }} - {{ task.type }} - todo - {{ task.startDate|localizeddate('medium', 'none') }} - {{ task.warningDate|localizeddate('medium', 'none') }} - {{ task.endDate|localizeddate('medium', 'none') }} - + +{% endmacro %} + +{% macro date_status(title, tasks, count, paginator) %} + {% if tasks|length > 0 %} +

{{ title|trans }}

+ + + + + + + + + + + + + + {% for task in tasks %} + + + + + + + + + {% endfor %} + +
{{ 'Title'|trans }}{{ 'Task type'|trans }}{{ 'Task status'|trans }}{{ 'Task start date'|trans }}{{ 'Task warning date'|trans }}{{ 'Task end date'|trans }}
{{ task.title }}{{ task.type }}todo{% if task.startDate is not null %}{{ task.startDate|localizeddate('medium', 'none') }}{% endif %}{% if task.warningDate is not null %}{{ task.warningDate|localizeddate('medium', 'none') }}{% endif %}{% if task.endDate is not null %}{{ task.endDate|localizeddate('medium', 'none') }}{% endif %}
+ + {% if tasks|length > paginator.getTotalItems %} + {{ chill_pagination(paginator) }} + {% endif %} + + {% endif %} {% endmacro %} {% import _self as helper %} {# filter tasks #} -{% set ended_tasks = [] %} -{% for task in single_tasks if (task.endDate|date("U") > 'now'|date("U")) %} - {% set ended_tasks = ended_tasks|merge([ task ]) %} -{% endfor %} - -{% set warning_tasks = [] %} -{% for task in single_tasks if (task.warningDate|date('U') > 'now'|date("U") and task not in ended_tasks) %} - {% set warning_tasks = warning_tasks|merge([ task ]) %} -{% endfor %} - -{% set rest_tasks = [] %} -{% for task in single_tasks if (task not in ended_tasks and task not in warning_tasks) %} - {% set rest_tasks = rest_tasks|merge([ task ]) %} -{% endfor %} - {% block personcontent %}

{{ 'Task list'|trans }}

- {% if ended_tasks|length > 0 %} -

{{ 'Task with expired deadline'|trans }}

- - {{ helper.thead() }} - - {% for task in ended_tasks %} - {{ helper.row(task) }} - {% endfor %} - -
+ {% 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) }} {% endif %} - {% if warning_tasks|length > 0 %} -

{{ 'Task with warning'|trans }}

- - {{ helper.thead() }} - - {% for task in warning_tasks %} - {{ helper.row(task) }} - {% endfor %} - -
+ {% 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) }} {% endif %} - {% if rest_tasks|length > 0 %} -

{{ 'Task '|trans }}

- - {{ helper.thead() }} - - {% for task in rest_tasks %} - {{ helper.row(task) }} - {% endfor %} - -
+ {% if single_task_current_tasks is defined %} + {{ helper.date_status('Current tasks', single_task_current_tasks, single_task_current_count, single_task_current_paginator) }} + {% 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) }} {% endif %} diff --git a/Tests/Controller/DefaultControllerTest.php b/Tests/Controller/DefaultControllerTest.php deleted file mode 100644 index a370af892..000000000 --- a/Tests/Controller/DefaultControllerTest.php +++ /dev/null @@ -1,17 +0,0 @@ -request('GET', '/'); - - $this->assertContains('Hello World', $client->getResponse()->getContent()); - } -}