centerResolverDispatcher = $centerResolverDispatcher; $this->em = $em; $this->security = $security; $this->authorizationHelper = $authorizationHelper; } public function buildBaseQuery( ?string $pattern = null, ?array $flags = [], ?array $types = [], ?array $users = [] ): QueryBuilder { $qb = $this->em->createQueryBuilder(); $qb ->from(SingleTask::class, 't'); if (null !== $pattern && '' !== $pattern) { $qb->andWhere($qb->expr()->like('LOWER(UNACCENT(t.title))', 'LOWER(UNACCENT(:pattern))')) ->setParameter('pattern', '%' . $pattern . '%'); } if (null !== $users && count($users) > 0) { $orXUser = $qb->expr()->orX(); foreach ($users as $key => $user) { $orXUser->add( $qb->expr()->eq('t.assignee', ':user_' . $key) ); $qb->setParameter('user_' . $key, $user); } if ($orXUser->count() > 0) { $qb->andWhere($orXUser); } } if (null !== $types && count($types) > 0) { $qb->andWhere($qb->expr()->in('t.type', ':types')); $qb->setParameter('types', $types); } if (null !== $flags && count($flags) > 0) { $orXDate = $qb->expr()->orX(); $orXState = $qb->expr()->orX(); $now = new DateTime(); foreach ($flags as $key => $flag) { switch ($flag) { case 'no-alert': $orXDate ->add( $qb->expr()->orX( $qb->expr()->isNull('t.endDate'), $qb->expr()->gte('t.endDate - COALESCE(t.warningInterval, :intervalBlank)', ':now') ) ); $qb ->setParameter('intervalBlank', new DateInterval('P0D')) ->setParameter('now', $now); break; case 'warning': $orXDate ->add( $qb->expr()->andX( $qb->expr()->not($qb->expr()->isNull('t.endDate')), $qb->expr()->not($qb->expr()->isNull('t.warningInterval')), $qb->expr()->lte('t.endDate - t.warningInterval', ':now'), $qb->expr()->gt('t.endDate', ':now') ) ); $qb ->setParameter('now', $now); break; case 'alert': $orXDate ->add( $qb->expr()->andX( $qb->expr()->not($qb->expr()->isNull('t.endDate')), $qb->expr()->lte('t.endDate', ':now') ) ); $qb ->setParameter('now', $now); break; case 'state_new': $orXState ->add( 'JSONB_ARRAY_LENGTH(t.currentStates) = 0' ); break; case substr($flag, 0, 6) === 'state_': $state = substr($flag, 6); $orXState ->add( "JSONB_EXISTS_IN_ARRAY(t.currentStates, :state_{$key}) = 'TRUE'" ); $qb->setParameter("state_{$key}", $state); break; default: throw new LogicException("this flag is not supported: {$flag}"); } } if ($orXDate->count() > 0) { $qb->andWhere($orXDate); } if ($orXState->count() > 0) { $qb->andWhere($orXState); } } return $qb; } public function buildQueryByCourse( AccompanyingPeriod $course, ?string $pattern = null, ?array $flags = [] ): QueryBuilder { $qb = $this->buildBaseQuery($pattern, $flags); return $qb ->andWhere($qb->expr()->eq('t.course', ':course')) ->setParameter('course', $course); } public function buildQueryByPerson( Person $person, ?string $pattern = null, ?array $flags = [] ): QueryBuilder { $qb = $this->buildBaseQuery($pattern, $flags); return $qb ->andWhere($qb->expr()->eq('t.person', ':person')) ->setParameter('person', $person); } public function buildQueryMyTasks( ?string $pattern = null, ?array $flags = [] ): QueryBuilder { $qb = $this->buildBaseQuery($pattern, $flags); return $qb ->andWhere($qb->expr()->eq('t.assignee', ':user')) ->setParameter('user', $this->security->getUser()); } public function countByAllViewable( ?string $pattern = null, ?array $flags = [], ?array $types = [], ?array $users = [] ): int { $qb = $this->buildBaseQuery($pattern, $flags, $types, $users); return $this ->addACLGlobal($qb) ->select('COUNT(t)') ->getQuery()->getSingleScalarResult(); } public function countByCourse( AccompanyingPeriod $course, ?string $pattern = null, ?array $flags = [] ): int { $qb = $this->buildQueryByCourse($course, $pattern, $flags); return $this ->addACL($qb, $course) ->select('COUNT(t)') ->getQuery()->getSingleScalarResult(); } public function countByCurrentUsersTasks( ?string $pattern = null, ?array $flags = [] ): int { return $this->buildQueryMyTasks($pattern, $flags) ->select('COUNT(t)') ->getQuery()->getSingleScalarResult(); } public function countByPerson( Person $person, ?string $pattern = null, ?array $flags = [] ): int { $qb = $this->buildQueryByPerson($person, $pattern, $flags); return $this ->addACL($qb, $person) ->select('COUNT(t)') ->getQuery()->getSingleScalarResult(); } public function findByAllViewable( ?string $pattern = null, ?array $flags = [], ?array $types = [], ?array $users = [], ?int $start = 0, ?int $limit = 50, ?array $orderBy = [] ): array { $qb = $this->buildBaseQuery($pattern, $flags, $types, $users); $qb = $this->addACLGlobal($qb); return $this->getResult($qb, $start, $limit, $orderBy); } public function findByCourse( AccompanyingPeriod $course, ?string $pattern = null, ?array $flags = [], ?int $start = 0, ?int $limit = 50, ?array $orderBy = [] ): array { $qb = $this->buildQueryByCourse($course, $pattern, $flags); $qb = $this->addACL($qb, $course); return $this->getResult($qb, $start, $limit, $orderBy); } public function findByCurrentUsersTasks( ?string $pattern = null, ?array $flags = [], ?int $start = 0, ?int $limit = 50, ?array $orderBy = [] ): array { $qb = $this->buildQueryMyTasks($pattern, $flags); return $this->getResult($qb, $start, $limit, $orderBy); } public function findByPerson( Person $person, ?string $pattern = null, ?array $flags = [], ?int $start = 0, ?int $limit = 50, ?array $orderBy = [] ): array { $qb = $this->buildQueryByPerson($person, $pattern, $flags); $qb = $this->addACL($qb, $person); return $this->getResult($qb, $start, $limit, $orderBy); } public function getResult( QueryBuilder $qb, ?int $start = 0, ?int $limit = 50, ?array $orderBy = [] ): array { $qb->select('t'); $qb ->setFirstResult($start) ->setMaxResults($limit); foreach ($orderBy as $field => $direction) { $qb->addOrderBy('t.' . $field, $direction); } return $qb->getQuery()->getResult(); } private function addACL( QueryBuilder $qb, $entity ): QueryBuilder { foreach ($this->centerResolverDispatcher->resolveCenters($entity) as $center) { $scopes = $this->authorizationHelper->getReachableScopes( $this->security->getUser(), TaskVoter::SHOW, $center ); $qb->andWhere($qb->expr()->in('t.circle', ':scopes')) ->setParameter('scopes', $scopes); } return $qb; } private function addACLGlobal( QueryBuilder $qb ): QueryBuilder { $allowedCenters = $this->authorizationHelper ->getReachableCenters($this->security->getUser(), TaskVoter::SHOW); if ([] === $allowedCenters) { $qb ->andWhere($qb->expr()->lt('t.id', ':falseid')) ->setParameter('falseid', -1); } $qb->leftJoin('t.person', 'person') ->leftJoin('t.course', 'course') ->leftJoin('course.participations', 'participation') ->leftJoin('participation.person', 'person_p') ->leftJoin('person.centerCurrent', 'center_current_person') ->leftJoin('person_p.centerCurrent', 'center_current_participation'); $qb->distinct(true); $k = 0; $orX = $qb->expr()->orX(); foreach ($allowedCenters as $center) { $allowedScopes = $this->authorizationHelper->getReachableScopes( $this->security->getUser(), TaskVoter::SHOW, $center ); $and = $qb->expr()->andX( $qb->expr()->orX( $qb->expr()->eq('center_current_person.center', ':center_' . $k), $qb->expr()->eq('center_current_participation.center', ':center_' . $k) ), $qb->expr()->in('t.circle', ':scopes_' . $k) ); $qb ->setParameter('center_' . $k, $center) ->setParameter('scopes_' . $k, $allowedScopes); $orX->add($and); ++$k; } $qb->andWhere($orX); return $qb; } }