. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License version 3. * * In accordance with Section 7(b) of the GNU Affero General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ namespace Espo\Modules\Crm\Tools\Activities; use Espo\Core\Acl; use Espo\Core\Exceptions\BadRequest; use Espo\Core\Exceptions\Error; use Espo\Core\Exceptions\NotFound; use Espo\Core\Exceptions\Forbidden; use Espo\Core\Name\Field; use Espo\Core\Select\Helpers\RelationQueryHelper; use Espo\Core\ServiceFactory; use Espo\Core\Templates\Entities\Company; use Espo\Core\Templates\Entities\Person; use Espo\Core\Utils\Config; use Espo\Core\Utils\Metadata; use Espo\Entities\Email; use Espo\Entities\User; use Espo\Modules\Crm\Entities\Account; use Espo\Modules\Crm\Entities\Call; use Espo\Modules\Crm\Entities\Contact; use Espo\Modules\Crm\Entities\Lead; use Espo\Modules\Crm\Entities\Meeting; use Espo\Modules\Crm\Entities\Reminder; use Espo\ORM\EntityCollection; use Espo\ORM\EntityManager; use Espo\ORM\Query\Part\Condition as Cond; use Espo\ORM\Query\Part\Expression as Expr; use Espo\ORM\Query\Part\Where\OrGroup; use Espo\ORM\Query\UnionBuilder; use Espo\ORM\Query\SelectBuilder; use Espo\ORM\Entity; use Espo\ORM\Query\Select; use Espo\ORM\Query\Part\Order; use Espo\Core\Acl\Table; use Espo\Core\Record\Collection as RecordCollection; use Espo\Core\Select\SearchParams; use Espo\Core\Select\SelectBuilderFactory; use Espo\Core\FieldProcessing\ListLoadProcessor; use Espo\Core\FieldProcessing\Loader\Params as FieldLoaderParams; use Espo\Core\Record\ServiceContainer as RecordServiceContainer; use Espo\Repositories\Email as EmailRepository; use LogicException; use PDO; use RuntimeException; class Service { /** @var array> */ private array $attributeMap = [ Email::ENTITY_TYPE => [ 'dateSent' => 'dateStart', ], ]; public function __construct( private ListLoadProcessor $listLoadProcessor, private RecordServiceContainer $recordServiceContainer, private SelectBuilderFactory $selectBuilderFactory, private Config $config, private Metadata $metadata, private Acl $acl, private ServiceFactory $serviceFactory, private EntityManager $entityManager, private User $user, private RelationQueryHelper $relationQueryHelper, ) {} protected function isPerson(string $scope): bool { return in_array( $scope, [Contact::ENTITY_TYPE, Lead::ENTITY_TYPE, User::ENTITY_TYPE] ) || $this->metadata->get(['scopes', $scope, 'type']) === Person::TEMPLATE_TYPE; } protected function isCompany(string $scope): bool { return $scope == Account::ENTITY_TYPE || $this->metadata->get(['scopes', $scope, 'type']) === Company::TEMPLATE_TYPE; } /** * @param string[] $statusList */ protected function getActivitiesUserMeetingQuery(User $entity, array $statusList = []): Select { try { $builder = $this->selectBuilderFactory ->create() ->from(Meeting::ENTITY_TYPE) ->withStrictAccessControl() ->buildQueryBuilder() ->select([ 'id', 'name', ['dateStart', 'dateStart'], ['dateEnd', 'dateEnd'], ['dateStartDate', 'dateStartDate'], ['dateEndDate', 'dateEndDate'], ['"Meeting"', '_scope'], 'assignedUserId', 'assignedUserName', 'parentType', 'parentId', 'status', Field::CREATED_AT, ['false', 'hasAttachment'], ['null', 'fromEmailAddressName'], ['null', 'fromString'], ]) ->leftJoin( 'MeetingUser', 'usersLeftMiddle', [ 'usersLeftMiddle.meetingId:' => 'meeting.id', ] ); } catch (BadRequest|Forbidden $e) { throw new RuntimeException($e->getMessage()); } $orBuilder = OrGroup::createBuilder(); $orBuilder->add( Cond::equal(Expr::column('usersLeftMiddle.userId'), $entity->getId()) ); if ($entity->isPortal() && $entity->getContactId()) { $orBuilder->add( $this->relationQueryHelper->prepareLinkWhereMany( Meeting::ENTITY_TYPE, 'contacts', $entity->getContactId() ) ); } $builder->where($orBuilder->build()); if (!empty($statusList)) { $builder->where([ 'status' => $statusList, ]); } return $builder->build(); } /** * @param string[] $statusList */ protected function getActivitiesUserCallQuery(User $entity, array $statusList = []): Select { $seed = $this->entityManager->getNewEntity(Call::ENTITY_TYPE); try { $builder = $this->selectBuilderFactory ->create() ->from(Call::ENTITY_TYPE) ->withStrictAccessControl() ->buildQueryBuilder() ->select([ 'id', 'name', ['dateStart', 'dateStart'], ['dateEnd', 'dateEnd'], ( $seed->hasAttribute('dateStartDate') ? ['dateStartDate', 'dateStartDate'] : ['null', 'dateStartDate'] ), ( $seed->hasAttribute('dateEndDate') ? ['dateEndDate', 'dateEndDate'] : ['null', 'dateEndDate'] ), ['"Call"', '_scope'], 'assignedUserId', 'assignedUserName', 'parentType', 'parentId', 'status', Field::CREATED_AT, ['false', 'hasAttachment'], ['null', 'fromEmailAddressName'], ['null', 'fromString'], ]) ->leftJoin( 'CallUser', 'usersLeftMiddle', [ 'usersLeftMiddle.callId:' => 'call.id', ] ); } catch (BadRequest|Forbidden $e) { throw new RuntimeException($e->getMessage()); } $orBuilder = OrGroup::createBuilder(); $orBuilder->add( Cond::equal(Expr::column('usersLeftMiddle.userId'), $entity->getId()) ); if ($entity->isPortal() && $entity->getContactId()) { $orBuilder->add( $this->relationQueryHelper->prepareLinkWhereMany( Call::ENTITY_TYPE, 'contacts', $entity->getContactId() ) ); } $builder->where($orBuilder->build()); if (!empty($statusList)) { $builder->where([ 'status' => $statusList, ]); } return $builder->build(); } /** * @param string[] $statusList * @return Select|Select[] */ protected function getActivitiesUserEmailQuery(User $entity, array $statusList = []) { if ($entity->isPortal() && $entity->get('contactId')) { $contact = $this->entityManager->getEntityById(Contact::ENTITY_TYPE, $entity->get('contactId')); if ($contact) { return $this->getActivitiesEmailQuery($contact, $statusList); } } try { $builder = $this->selectBuilderFactory ->create() ->from(Email::ENTITY_TYPE) ->withStrictAccessControl() ->buildQueryBuilder() ->select([ 'id', 'name', ['dateSent', 'dateStart'], ['null', 'dateEnd'], ['null', 'dateStartDate'], ['null', 'dateEndDate'], ['"Email"', '_scope'], 'assignedUserId', 'assignedUserName', 'parentType', 'parentId', 'status', Field::CREATED_AT, 'hasAttachment', 'fromEmailAddressName', 'fromString', ]) ->leftJoin( 'EmailUser', 'usersLeftMiddle', [ 'usersLeftMiddle.emailId:' => 'email.id', ] ) ->where([ 'usersLeftMiddle.userId' => $entity->getId(), ]); } catch (BadRequest|Forbidden $e) { throw new RuntimeException($e->getMessage()); } if (!empty($statusList)) { $builder->where([ 'status' => $statusList, ]); } return $builder->build(); } /** * @param string[] $statusList * @return Select|Select[] */ protected function getActivitiesMeetingOrCallQuery( Entity $entity, array $statusList, string $targetEntityType ) { if ($entity instanceof User && $targetEntityType === Meeting::ENTITY_TYPE) { return $this->getActivitiesUserMeetingQuery($entity, $statusList); } if ($entity instanceof User && $targetEntityType === Call::ENTITY_TYPE) { return $this->getActivitiesUserCallQuery($entity, $statusList); } $entityType = $entity->getEntityType(); $id = $entity->getId(); $seed = $this->entityManager->getNewEntity($targetEntityType); try { $baseBuilder = $this->selectBuilderFactory ->create() ->from($targetEntityType) ->withStrictAccessControl() ->buildQueryBuilder() ->select([ 'id', 'name', ['dateStart', 'dateStart'], ['dateEnd', 'dateEnd'], ( $seed->hasAttribute('dateStartDate') ? ['dateStartDate', 'dateStartDate'] : ['null', 'dateStartDate'] ), ( $seed->hasAttribute('dateEndDate') ? ['dateEndDate', 'dateEndDate'] : ['null', 'dateEndDate'] ), ['"' . $targetEntityType . '"', '_scope'], 'assignedUserId', 'assignedUserName', 'parentType', 'parentId', 'status', Field::CREATED_AT, ['false', 'hasAttachment'], ['null', 'fromEmailAddressName'], ['null', 'fromString'], ]); } catch (BadRequest|Forbidden $e) { throw new RuntimeException($e->getMessage()); } if (!empty($statusList)) { $baseBuilder->where([ 'status' => $statusList, ]); } $builder = clone $baseBuilder; if ($entityType === Account::ENTITY_TYPE) { $builder->where([ 'OR' => [ [ 'parentId' => $id, 'parentType' => Account::ENTITY_TYPE, ], [ 'accountId' => $id, ], ], ]); } else if ($entityType === Lead::ENTITY_TYPE && $entity->get('createdAccountId')) { $builder->where([ 'OR' => [ [ 'parentId' => $id, 'parentType' => Lead::ENTITY_TYPE, ], [ 'accountId' => $entity->get('createdAccountId'), ], ], ]); } else { $builder->where([ 'parentId' => $id, 'parentType' => $entityType, ]); } if (!$this->isPerson($entityType)) { return $builder->build(); } $queryList = [$builder->build()]; /** @var ?string $link */ $link = $this->metadata->get("scopes.$targetEntityType.attendeeLinkMap.$entityType"); if (!$link) { return $queryList; } $personBuilder = clone $baseBuilder; $personBuilder ->join($link) ->where([ $link . '.id' => $id, 'OR' => [ 'parentType!=' => $entityType, 'parentId!=' => $id, 'parentType' => null, 'parentId' => null, ], ]); $queryList[] = $personBuilder->build(); return $queryList; } /** * @param string[] $statusList * @return Select|Select[] */ protected function getActivitiesMeetingQuery(Entity $entity, array $statusList = []) { return $this->getActivitiesMeetingOrCallQuery($entity, $statusList, Meeting::ENTITY_TYPE); } /** * @param string[] $statusList * @return Select|Select[] */ protected function getActivitiesCallQuery(Entity $entity, array $statusList = []) { return $this->getActivitiesMeetingOrCallQuery($entity, $statusList, Call::ENTITY_TYPE); } /** * @param string[] $statusList * @return Select|Select[] */ protected function getActivitiesEmailQuery(Entity $entity, array $statusList = []) { if ($entity instanceof User) { return $this->getActivitiesUserEmailQuery($entity, $statusList); } $entityType = $entity->getEntityType(); $id = $entity->getId(); try { $baseBuilder = $this->selectBuilderFactory ->create() ->from('Email') ->withStrictAccessControl() ->buildQueryBuilder() ->select([ 'id', 'name', ['dateSent', 'dateStart'], ['null', 'dateEnd'], ['null', 'dateStartDate'], ['null', 'dateEndDate'], ['"Email"', '_scope'], 'assignedUserId', 'assignedUserName', 'parentType', 'parentId', 'status', Field::CREATED_AT, 'hasAttachment', 'fromEmailAddressName', 'fromString', ]); } catch (BadRequest|Forbidden $e) { throw new RuntimeException($e->getMessage()); } if (!empty($statusList)) { $baseBuilder->where([ 'status' => $statusList, ]); } $builder = clone $baseBuilder; if ($entityType === Account::ENTITY_TYPE) { $builder->where([ 'OR' => [ [ 'parentId' => $id, 'parentType' => Account::ENTITY_TYPE, ], [ 'accountId' => $id, ], ], ]); } else if ($entityType == Lead::ENTITY_TYPE && $entity->get('createdAccountId')) { $builder->where([ 'OR' => [ [ 'parentId' => $id, 'parentType' => Lead::ENTITY_TYPE, ], [ 'accountId' => $entity->get('createdAccountId'), ], ], ]); } else { $builder->where([ 'parentId' => $id, 'parentType' => $entityType, ]); } if (!$this->isPerson($entityType) && !$this->isCompany($entityType)) { return $builder->build(); } $queryList = [$builder->build()]; $personBuilder = clone $baseBuilder; $personBuilder ->leftJoin( 'EntityEmailAddress', 'eea', [ 'eea.emailAddressId:' => 'fromEmailAddressId', 'eea.entityType' => $entityType, 'eea.deleted' => false, ] ) ->where([ 'OR' => [ 'parentType!=' => $entityType, 'parentId!=' => $id, 'parentType' => null, 'parentId' => null, ], 'eea.entityId' => $id, ]); $queryList[] = $personBuilder->build(); $addressBuilder = clone $baseBuilder; $addressBuilder ->leftJoin( 'EmailEmailAddress', 'em', [ 'em.emailId:' => 'id', 'em.deleted' => false, ] ) ->leftJoin( 'EntityEmailAddress', 'eea', [ 'eea.emailAddressId:' => 'em.emailAddressId', 'eea.entityType' => $entityType, 'eea.deleted' => false, ] ) ->where([ 'OR' => [ 'parentType!=' => $entityType, 'parentId!=' => $id, 'parentType' => null, 'parentId' => null, ], 'eea.entityId' => $id, ]); $queryList[] = $addressBuilder->build(); return $queryList; } /** * @param array> $parts * @return RecordCollection */ protected function getResultFromQueryParts(array $parts, string $scope, FetchParams $params): RecordCollection { if ($parts === []) { /** @var RecordCollection */ return new RecordCollection(new EntityCollection(), 0); } $onlyScope = false; if ($params->getEntityType()) { $onlyScope = $params->getEntityType(); } $maxSize = $params->getMaxSize(); $queryList = []; if (!$onlyScope) { foreach ($parts as $part) { if (is_array($part)) { $queryList = array_merge($queryList, $part); } else { $queryList[] = $part; } } } else { $part = $parts[$onlyScope]; if (is_array($part)) { $queryList = array_merge($queryList, $part); } else { $queryList[] = $part; } } $maxSizeQ = $maxSize; $offset = $params->getOffset() ?? 0; if (!$onlyScope && $scope === User::ENTITY_TYPE) { // optimizing sub-queries $newQueryList = []; foreach ($queryList as $query) { $subBuilder = $this->entityManager ->getQueryBuilder() ->select() ->clone($query); if ($maxSize) { $subBuilder->limit(0, $offset + $maxSize + 1); } // Order by `dateStart`. $subBuilder->order( Order ::create( $query->getSelect()[2]->getExpression() ) ->withDesc() ); $newQueryList[] = $subBuilder->build(); } $queryList = $newQueryList; } $builder = $this->entityManager ->getQueryBuilder() ->union(); foreach ($queryList as $query) { $builder->query($query); } $totalCount = -2; if ($scope !== User::ENTITY_TYPE) { $countQuery = $this->entityManager ->getQueryBuilder() ->select() ->fromQuery($builder->build(), 'c') ->select('COUNT:(c.id)', 'count') ->build(); $sth = $this->entityManager->getQueryExecutor()->execute($countQuery); $row = $sth->fetch(PDO::FETCH_ASSOC); $totalCount = $row['count']; } $builder->order('dateStart', 'DESC'); if ($scope === User::ENTITY_TYPE) { $maxSizeQ++; } else { $builder->order(Field::CREATED_AT, 'DESC'); } if ($maxSize) { $builder->limit($offset, $maxSizeQ); } $unionQuery = $builder->build(); $sth = $this->entityManager->getQueryExecutor()->execute($unionQuery); $rowList = $sth->fetchAll(PDO::FETCH_ASSOC) ?: []; $collection = new EntityCollection(); foreach ($rowList as $row) { $itemEntityType = $row['_scope'] ?? null; if (!is_string($itemEntityType)) { throw new LogicException(); } foreach (($this->attributeMap[$itemEntityType] ?? []) as $attributeActual => $attribute) { $row[$attributeActual] = $row[$attribute]; unset($row[$attribute]); } $itemEntity = $this->entityManager->getNewEntity($itemEntityType); $itemEntity->setMultiple($row); $itemEntity->setAsFetched(); $this->prepareEntity($itemEntity); $collection->append($itemEntity); } if ($scope === User::ENTITY_TYPE) { return RecordCollection::createNoCount($collection, $maxSize); } return RecordCollection::create($collection, $totalCount); } private function prepareEntity(Entity $entity): void { if ($entity instanceof Email) { $repo = $this->entityManager->getRepository(Email::ENTITY_TYPE); if (!$repo instanceof EmailRepository) { throw new RuntimeException(); } $repo->loadFromField($entity); $repo->loadNameHash($entity, [Email::ADDRESS_FROM]); } } /** * @throws Forbidden */ protected function accessCheck(Entity $entity): void { if ($entity instanceof User) { if (!$this->acl->checkUserPermission($entity, Acl\Permission::USER_CALENDAR)) { throw new Forbidden(); } return; } if (!$this->acl->check($entity, Table::ACTION_READ)) { throw new Forbidden(); } } /** * @return RecordCollection * @throws Forbidden * @throws NotFound * @throws Error * @throws BadRequest */ public function findActivitiesEntityType( string $scope, string $id, string $entityType, bool $isHistory, SearchParams $searchParams ): RecordCollection { if (!$this->acl->checkScope($entityType)) { throw new Forbidden(); } $entity = $this->entityManager->getEntityById($scope, $id); if (!$entity) { throw new NotFound(); } $this->accessCheck($entity); if (!$this->metadata->get(['scopes', $entityType, 'activity'])) { throw new Error("Entity '$entityType' is not an activity."); } if (!$isHistory) { $statusList = $this->metadata->get(['scopes', $entityType, 'activityStatusList']) ?? [Meeting::STATUS_PLANNED]; } else { $statusList = $this->metadata->get(['scopes', $entityType, 'historyStatusList']) ?? [Meeting::STATUS_HELD, Meeting::STATUS_NOT_HELD]; } if ($entityType === Email::ENTITY_TYPE && $searchParams->getOrderBy() === 'dateStart') { $searchParams = $searchParams->withOrderBy('dateSent'); } $service = $this->recordServiceContainer->get($entityType); $preparedSearchParams = $service->prepareSearchParams($searchParams); $offset = $searchParams->getOffset(); $limit = $searchParams->getMaxSize(); $baseQueryList = $this->getActivitiesQuery($entity, $entityType, $statusList); if (!is_array($baseQueryList)) { $baseQueryList = [$baseQueryList]; } $queryList = []; $order = null; foreach ($baseQueryList as $i => $itemQuery) { $itemBuilder = $this->selectBuilderFactory ->create() ->clone($itemQuery) ->withSearchParams($preparedSearchParams) ->withComplexExpressionsForbidden() ->withWherePermissionCheck() ->buildQueryBuilder(); if ($i === 0) { $order = $itemBuilder->build()->getOrder(); } /** @noinspection PhpRedundantOptionalArgumentInspection */ $itemBuilder ->limit(null, null) ->order([]); $queryList[] = $itemBuilder->build(); } $query = $queryList[0]; if (count($queryList) > 1) { $unionBuilder = $this->entityManager ->getQueryBuilder() ->union(); foreach ($queryList as $subQuery) { $unionBuilder->query($subQuery); } if ($order !== null && count($order)) { $unionBuilder->order( $order[0]->getExpression()->getValue(), $order[0]->getDirection() ); } $query = $unionBuilder->build(); } /** @var UnionBuilder|SelectBuilder $builder */ $builder = $this->entityManager ->getQueryBuilder() ->clone($query); if ($order && count($queryList) === 1) { $builder->order($order); } $builder->limit($offset, $limit); $sql = $this->entityManager->getQueryComposer()->compose($builder->build()); $collection = $this->entityManager ->getCollectionFactory() ->createFromSthCollection( $this->entityManager ->getRDBRepository($entityType) ->findBySql($sql) ); $loadProcessorParams = FieldLoaderParams ::create() ->withSelect($searchParams->getSelect()); foreach ($collection as $e) { $this->listLoadProcessor->process($e, $loadProcessorParams); $service->prepareEntityForOutput($e); } $countQuery = $this->entityManager->getQueryBuilder() ->select() ->fromQuery($query, 'c') ->select('COUNT:(c.id)', 'count') ->build(); $sth = $this->entityManager->getQueryExecutor()->execute($countQuery); $row = $sth->fetch(PDO::FETCH_ASSOC); $total = $row['count']; return new RecordCollection($collection, $total); } /** * @return RecordCollection * @throws NotFound * @throws Error * @throws Forbidden */ public function getActivities(string $scope, string $id, FetchParams $params): RecordCollection { $entity = $this->entityManager->getEntityById($scope, $id); if (!$entity) { throw new NotFound(); } $this->accessCheck($entity); $targetScope = $params->getEntityType(); $fetchAll = empty($targetScope); if ($targetScope) { if (!$this->metadata->get(['scopes', $targetScope, 'activity'])) { throw new Error('Entity \'' . $targetScope . '\' is not an activity'); } } $parts = []; /** @var string[] $entityTypeList */ $entityTypeList = $this->config->get('activitiesEntityList') ?? [Meeting::ENTITY_TYPE, Call::ENTITY_TYPE]; foreach ($entityTypeList as $entityType) { if (!$fetchAll && $targetScope !== $entityType) { continue; } if (!$this->acl->checkScope($entityType)) { continue; } if (!$this->metadata->get('scopes.' . $entityType . '.activity')) { continue; } $statusList = $this->metadata->get(['scopes', $entityType, 'activityStatusList']) ?? [Meeting::STATUS_PLANNED]; $parts[$entityType] = $this->getActivitiesQuery($entity, $entityType, $statusList); } return $this->getResultFromQueryParts($parts, $scope, $params); } /** * @return RecordCollection * @throws NotFound * @throws Error * @throws Forbidden */ public function getHistory(string $scope, string $id, FetchParams $params): RecordCollection { $entity = $this->entityManager->getEntityById($scope, $id); if (!$entity) { throw new NotFound(); } $this->accessCheck($entity); $targetScope = $params->getEntityType(); $fetchAll = empty($targetScope); if ($targetScope) { if (!$this->metadata->get(['scopes', $targetScope, 'activity'])) { throw new Error('Entity \'' . $targetScope . '\' is not an activity'); } } $parts = []; /** @var string[] $entityTypeList */ $entityTypeList = $this->config->get('historyEntityList') ?? [ Meeting::ENTITY_TYPE, Call::ENTITY_TYPE, Email::ENTITY_TYPE ]; foreach ($entityTypeList as $entityType) { if (!$fetchAll && $targetScope !== $entityType) { continue; } if (!$this->acl->checkScope($entityType)) { continue; } if (!$this->metadata->get('scopes.' . $entityType . '.activity')) { continue; } $statusList = $this->metadata->get(['scopes', $entityType, 'historyStatusList']) ?? [Meeting::STATUS_HELD, Meeting::STATUS_NOT_HELD]; $parts[$entityType] = $this->getActivitiesQuery($entity, $entityType, $statusList); } return $this->getResultFromQueryParts($parts, $scope, $params); } /** * @param string[] $statusList * @return Select|Select[] */ protected function getActivitiesQuery(Entity $entity, string $scope, array $statusList = []) { $serviceName = 'Activities' . $entity->getEntityType(); if ($this->serviceFactory->checkExists($serviceName)) { // For bc. $service = $this->serviceFactory->create($serviceName); $methodName = 'getActivities' . $scope . 'Query'; if (method_exists($service, $methodName)) { return $service->$methodName($entity, $statusList); } } if ($scope === Meeting::ENTITY_TYPE) { return $this->getActivitiesMeetingQuery($entity, $statusList); } if ($scope === Call::ENTITY_TYPE) { return $this->getActivitiesCallQuery($entity, $statusList); } if ($scope === Email::ENTITY_TYPE) { return $this->getActivitiesEmailQuery($entity, $statusList); } return $this->getActivitiesBaseQuery($entity, $scope, $statusList); } /** * @param string[] $statusList */ protected function getActivitiesBaseQuery(Entity $entity, string $scope, array $statusList = []): Select { $seed = $this->entityManager->getNewEntity($scope); try { $builder = $this->selectBuilderFactory ->create() ->from($scope) ->withStrictAccessControl() ->buildQueryBuilder() ->select([ 'id', 'name', ($seed->hasAttribute('dateStart') ? ['dateStart', 'dateStart'] : ['null', 'dateStart']), ($seed->hasAttribute('dateEnd') ? ['dateEnd', 'dateEnd'] : ['null', 'dateEnd']), ($seed->hasAttribute('dateStartDate') ? ['dateStartDate', 'dateStartDate'] : ['null', 'dateStartDate']), ($seed->hasAttribute('dateEndDate') ? ['dateEndDate', 'dateEndDate'] : ['null', 'dateEndDate']), ['"' . $scope . '"', '_scope'], ($seed->hasAttribute('assignedUserId') ? ['assignedUserId', 'assignedUserId'] : ['null', 'assignedUserId']), ($seed->hasAttribute('assignedUserName') ? ['assignedUserName', 'assignedUserName'] : ['null', 'assignedUserName']), ($seed->hasAttribute('parentType') ? ['parentType', 'parentType'] : ['null', 'parentType']), ($seed->hasAttribute('parentId') ? ['parentId', 'parentId'] : ['null', 'parentId']), 'status', Field::CREATED_AT, ['false', 'hasAttachment'], ['null', 'fromEmailAddressName'], ['null', 'fromString'], ]); } catch (BadRequest|Forbidden $e) { throw new RuntimeException($e->getMessage()); } if ($entity->getEntityType() === User::ENTITY_TYPE) { $builder->where([ 'assignedUserId' => $entity->getId(), ]); } else { $builder->where([ 'parentId' => $entity->getId(), 'parentType' => $entity->getEntityType(), ]); } $builder->where([ 'status' => $statusList, ]); return $builder->build(); } public function removeReminder(string $id): void { $builder = $this->entityManager ->getQueryBuilder() ->delete() ->from(Reminder::ENTITY_TYPE) ->where([ 'id' => $id, ]); if (!$this->user->isAdmin()) { $builder->where([ 'userId' => $this->user->getId(), ]); } $deleteQuery = $builder->build(); $this->entityManager->getQueryExecutor()->execute($deleteQuery); } }