ref
This commit is contained in:
@@ -31,12 +31,14 @@ namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
use Espo\Core\{
|
||||
Api\Request,
|
||||
Exceptions\Forbidden,
|
||||
Exceptions\NotFound,
|
||||
Record\SearchParamsFetcher};
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Field\DateTime;
|
||||
use Espo\Core\Record\SearchParamsFetcher;
|
||||
|
||||
use Espo\Entities\User as UserEntity;
|
||||
use Espo\Tools\Stream\FindParams;
|
||||
use Espo\Tools\Stream\Service as Service;
|
||||
|
||||
use stdClass;
|
||||
@@ -63,27 +65,35 @@ class Stream
|
||||
*/
|
||||
public function getActionList(Request $request): stdClass
|
||||
{
|
||||
$params = $request->getRouteParams();
|
||||
$id = $request->getRouteParam('id');
|
||||
$scope = $request->getRouteParam('scope');
|
||||
|
||||
$scope = $params['scope'];
|
||||
$id = isset($params['id']) ? $params['id'] : null;
|
||||
if ($scope === null) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if ($id === null && $scope !== UserEntity::ENTITY_TYPE) {
|
||||
throw new BadRequest("No ID.");
|
||||
}
|
||||
|
||||
$searchParams = $this->searchParamsFetcher->fetch($request);
|
||||
|
||||
$offset = $searchParams->getOffset();
|
||||
$maxSize = $searchParams->getMaxSize();
|
||||
|
||||
$after = $request->getQueryParam('after');
|
||||
$filter = $request->getQueryParam('filter');
|
||||
$skipOwn = $request->getQueryParam('skipOwn') === 'true';
|
||||
|
||||
$result = $this->service->find($scope, $id, [
|
||||
'offset' => $offset,
|
||||
'maxSize' => $maxSize,
|
||||
'after' => $after,
|
||||
'filter' => $filter,
|
||||
'skipOwn' => $skipOwn,
|
||||
]);
|
||||
/** @todo Use named params. */
|
||||
$findParams = new FindParams(
|
||||
$searchParams,
|
||||
$skipOwn,
|
||||
$after ?
|
||||
DateTime::fromString($after) : null,
|
||||
$filter
|
||||
);
|
||||
|
||||
$result = $scope === UserEntity::ENTITY_TYPE ?
|
||||
$this->service->findUserStream($id, $findParams) :
|
||||
$this->service->find($scope, $id ?? '', $findParams);
|
||||
|
||||
return (object) [
|
||||
'total' => $result->getTotal(),
|
||||
@@ -98,30 +108,33 @@ class Stream
|
||||
*/
|
||||
public function getActionListPosts(Request $request): stdClass
|
||||
{
|
||||
$params = $request->getRouteParams();
|
||||
$id = $request->getRouteParam('id');
|
||||
$scope = $request->getRouteParam('scope');
|
||||
|
||||
$scope = $params['scope'];
|
||||
$id = isset($params['id']) ? $params['id'] : null;
|
||||
|
||||
$searchParams = $this->searchParamsFetcher->fetch($request);
|
||||
|
||||
$offset = $searchParams->getOffset();
|
||||
$maxSize = $searchParams->getMaxSize();
|
||||
|
||||
$after = $request->getQueryParam('after');
|
||||
$where = $request->getQueryParams()['where'] ?? null;
|
||||
|
||||
if ($where !== null && !is_array($where)) {
|
||||
if ($scope === null) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$result = $this->service->find($scope, $id, [
|
||||
'offset' => $offset,
|
||||
'maxSize' => $maxSize,
|
||||
'after' => $after,
|
||||
'filter' => 'posts',
|
||||
'where' => $where,
|
||||
]);
|
||||
if ($id === null && $scope !== UserEntity::ENTITY_TYPE) {
|
||||
throw new BadRequest("No ID.");
|
||||
}
|
||||
|
||||
$after = $request->getQueryParam('after');
|
||||
|
||||
$searchParams = $this->searchParamsFetcher->fetch($request);
|
||||
|
||||
/** @todo Use named params. */
|
||||
$findParams = new FindParams(
|
||||
$searchParams,
|
||||
false,
|
||||
$after ?
|
||||
DateTime::fromString($after) : null,
|
||||
FindParams::FILTER_POSTS
|
||||
);
|
||||
|
||||
$result = $scope === UserEntity::ENTITY_TYPE ?
|
||||
$this->service->findUserStream($id, $findParams) :
|
||||
$this->service->find($scope, $id ?? '', $findParams);
|
||||
|
||||
return (object) [
|
||||
'total' => $result->getTotal(),
|
||||
|
||||
@@ -45,7 +45,6 @@ class SearchParamsFetcher
|
||||
private const MAX_SIZE_LIMIT = 200;
|
||||
|
||||
private Config $config;
|
||||
|
||||
private TextMetadataProvider $textMetadataProvider;
|
||||
|
||||
public function __construct(Config $config, TextMetadataProvider $textMetadataProvider)
|
||||
|
||||
@@ -37,16 +37,15 @@ use stdClass;
|
||||
|
||||
/**
|
||||
* Search parameters.
|
||||
*
|
||||
* @immutable
|
||||
*/
|
||||
class SearchParams
|
||||
{
|
||||
/**
|
||||
* @var array<string,mixed>
|
||||
*/
|
||||
/** @var array<string,mixed> */
|
||||
private array $rawParams = [];
|
||||
|
||||
public const ORDER_ASC = 'ASC';
|
||||
|
||||
public const ORDER_DESC = 'DESC';
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
@@ -46,9 +46,7 @@ class Applier
|
||||
private string $entityType;
|
||||
|
||||
private User $user;
|
||||
|
||||
private ConverterFactory $converterFactory;
|
||||
|
||||
private CheckerFactory $checkerFactory;
|
||||
|
||||
public function __construct(
|
||||
|
||||
@@ -35,21 +35,18 @@ use Espo\Core\Select\Where\Item\Data\DateTime as DateTimeData;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @immutable
|
||||
*/
|
||||
class Item
|
||||
{
|
||||
public const TYPE_AND = Item\Type::AND;
|
||||
|
||||
public const TYPE_OR = Item\Type::OR;
|
||||
|
||||
private string $type;
|
||||
|
||||
private ?string $attribute = null;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
/** @var mixed */
|
||||
private $value = null;
|
||||
|
||||
private ?Data $data = null;
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,118 +32,61 @@ namespace Espo\Core\Select\Where\Item;
|
||||
class Type
|
||||
{
|
||||
public const AND = 'and';
|
||||
|
||||
public const OR = 'or';
|
||||
|
||||
public const NOT = 'not';
|
||||
|
||||
public const SUBQUERY_NOT_IN = 'subQueryNotIn';
|
||||
|
||||
public const SUBQUERY_IN = 'subQueryIn';
|
||||
|
||||
public const EXPRESSION = 'expression';
|
||||
|
||||
public const IN = 'in';
|
||||
|
||||
public const NOT_IN = 'notIn';
|
||||
|
||||
public const EQUALS = 'equals';
|
||||
|
||||
public const NOT_EQUALS = 'notEquals';
|
||||
|
||||
public const ON = 'on';
|
||||
|
||||
public const NOT_ON = 'notOn';
|
||||
|
||||
public const LIKE = 'like';
|
||||
|
||||
public const NOT_LIKE = 'notLike';
|
||||
|
||||
public const STARTS_WITH = 'startsWith';
|
||||
|
||||
public const ENDS_WITH = 'endsWith';
|
||||
|
||||
public const CONTAINS = 'contains';
|
||||
|
||||
public const GREATER_THAN = 'greaterThan';
|
||||
|
||||
public const LESS_THAN = 'lessThan';
|
||||
|
||||
public const GREATER_THAN_OR_EQUALS = 'greaterThanOrEquals';
|
||||
|
||||
public const LESS_THAN_OR_EQUALS = 'lessThanOrEquals';
|
||||
|
||||
public const AFTER = 'after';
|
||||
|
||||
public const BEFORE = 'before';
|
||||
|
||||
public const BETWEEN = 'between';
|
||||
|
||||
public const ANY = 'any';
|
||||
|
||||
public const NONE = 'none';
|
||||
|
||||
public const IS_NULL = 'isNull';
|
||||
|
||||
public const IS_NOT_NULL = 'isNotNull';
|
||||
|
||||
public const IS_TRUE = 'isTrue';
|
||||
|
||||
public const IS_FALSE = 'isFalse';
|
||||
|
||||
public const TODAY = 'today';
|
||||
|
||||
public const PAST = 'past';
|
||||
|
||||
public const FUTURE = 'future';
|
||||
|
||||
public const LAST_X_DAYS = 'lastXDays';
|
||||
|
||||
public const NEXT_X_DAYS = 'nextXDays';
|
||||
|
||||
public const OLDER_THAN_X_DAYS = 'olderThanXDays';
|
||||
|
||||
public const AFTER_X_DAYS = 'afterXDays';
|
||||
|
||||
public const CURRENT_MONTH = 'currentMonth';
|
||||
|
||||
public const NEXT_MONTH = 'nextMonth';
|
||||
|
||||
public const LAST_MONTH = 'lastMonth';
|
||||
|
||||
public const CURRENT_QUARTER = 'currentQuarter';
|
||||
|
||||
public const LAST_QUARTER = 'lastQuarter';
|
||||
|
||||
public const CURRENT_YEAR = 'currentYear';
|
||||
|
||||
public const LAST_YEAR = 'lastYear';
|
||||
|
||||
public const CURRENT_FISCAL_YEAR = 'currentFiscalYear';
|
||||
|
||||
public const LAST_FISCAL_YEAR = 'lastFiscalYear';
|
||||
|
||||
public const CURRENT_FISCAL_QUARTER = 'currentFiscalQuarter';
|
||||
|
||||
public const LAST_FISCAL_QUARTER = 'lastFiscalQuarter';
|
||||
|
||||
public const ARRAY_ANY_OF = 'arrayAnyOf';
|
||||
|
||||
public const ARRAY_NONE_OF = 'arrayNoneOf';
|
||||
|
||||
public const ARRAY_ALL_OF = 'arrayAllOf';
|
||||
|
||||
public const ARRAY_IS_EMPTY = 'arrayIsEmpty';
|
||||
|
||||
public const ARRAY_IS_NOT_EMPTY = 'arrayIsNotEmpty';
|
||||
|
||||
public const IS_LINKED_WITH = 'linkedWith';
|
||||
|
||||
public const IS_NOT_LINKED_WITH = 'notLinkedWith';
|
||||
|
||||
public const IS_LINKED_WITH_ALL = 'linkedWithAll';
|
||||
|
||||
public const IS_LINKED_WITH_ANY = 'isLinked';
|
||||
|
||||
public const IS_LINKED_WITH_NONE = 'isNotLinked';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* 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 General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\Stream;
|
||||
|
||||
use Espo\Core\Field\DateTime;
|
||||
use Espo\Core\Select\SearchParams;
|
||||
use Espo\Core\Select\Where\Item as WhereItem;
|
||||
|
||||
/**
|
||||
* @immutable
|
||||
*/
|
||||
class FindParams
|
||||
{
|
||||
public const FILTER_POSTS = 'posts';
|
||||
public const FILTER_UPDATES = 'updates';
|
||||
|
||||
private SearchParams $searchParams;
|
||||
private bool $skipOwn;
|
||||
private ?DateTime $after;
|
||||
private ?string $filter;
|
||||
|
||||
public function __construct(
|
||||
SearchParams $searchParams,
|
||||
bool $skipOwn = false,
|
||||
?DateTime $after = null,
|
||||
?string $filter = null,
|
||||
) {
|
||||
$this->searchParams = $searchParams;
|
||||
$this->skipOwn = $skipOwn;
|
||||
$this->after = $after;
|
||||
$this->filter = $filter;
|
||||
}
|
||||
|
||||
public function getSearchParams(): SearchParams
|
||||
{
|
||||
return $this->searchParams;
|
||||
}
|
||||
|
||||
public function getMaxSize(): ?int
|
||||
{
|
||||
return $this->searchParams->getMaxSize();
|
||||
}
|
||||
|
||||
public function getOffset(): ?int
|
||||
{
|
||||
return $this->searchParams->getOffset();
|
||||
}
|
||||
|
||||
public function skipOwn(): bool
|
||||
{
|
||||
return $this->skipOwn;
|
||||
}
|
||||
|
||||
public function getAfter(): ?DateTime
|
||||
{
|
||||
return $this->after;
|
||||
}
|
||||
|
||||
public function getFilter(): ?string
|
||||
{
|
||||
return $this->filter;
|
||||
}
|
||||
}
|
||||
@@ -388,22 +388,16 @@ class Service
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{
|
||||
* offset?: int|null,
|
||||
* maxSize: int|null,
|
||||
* skipOwn?: bool,
|
||||
* where?: ?array<mixed,mixed>,
|
||||
* after?: ?string,
|
||||
* filter?: ?string,
|
||||
* } $params
|
||||
* @throws NotFound
|
||||
* @throws Forbidden
|
||||
* @return RecordCollection<Note>
|
||||
*/
|
||||
public function findUserStream(string $userId, array $params): RecordCollection
|
||||
public function findUserStream(?string $userId, FindParams $params): RecordCollection
|
||||
{
|
||||
$offset = intval($params['offset'] ?? 0);
|
||||
$maxSize = intval($params['maxSize']);
|
||||
$userId ??= $this->user->getId();
|
||||
|
||||
$offset = $params->getOffset() ?? 0;
|
||||
$maxSize = $params->getMaxSize();
|
||||
|
||||
$sqLimit = $offset + $maxSize + 1;
|
||||
|
||||
@@ -412,15 +406,13 @@ class Service
|
||||
$this->entityManager->getRDBRepositoryByClass(User::class)->getById($userId);
|
||||
|
||||
if (!$user) {
|
||||
throw new NotFound();
|
||||
throw new NotFound("User not found.");
|
||||
}
|
||||
|
||||
if (!$this->acl->checkUserPermission($user, 'user')) {
|
||||
throw new Forbidden();
|
||||
throw new Forbidden("No user permission access.");
|
||||
}
|
||||
|
||||
$skipOwn = $params['skipOwn'] ?? false;
|
||||
|
||||
$teamIdList = $user->getTeamIdList();
|
||||
|
||||
$select = [
|
||||
@@ -447,11 +439,14 @@ class Service
|
||||
|
||||
$additionalQuery = null;
|
||||
|
||||
if (!empty($params['where'])) {
|
||||
$searchParams = SearchParams::fromRaw([
|
||||
'where' => $params['where'],
|
||||
]);
|
||||
$searchParams = $params->getSearchParams();
|
||||
|
||||
if (
|
||||
$searchParams->getWhere() ||
|
||||
$searchParams->getTextFilter() ||
|
||||
$searchParams->getPrimaryFilter() ||
|
||||
$searchParams->getBoolFilterList() !== []
|
||||
) {
|
||||
$additionalQuery = $this->selectBuilderFactory
|
||||
->create()
|
||||
->from(Note::ENTITY_TYPE)
|
||||
@@ -780,7 +775,7 @@ class Service
|
||||
->build();
|
||||
}
|
||||
|
||||
if ($skipOwn) {
|
||||
if ($params->skipOwn()) {
|
||||
foreach ($queryList as $i => $query) {
|
||||
$queryList[$i] = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
@@ -803,7 +798,7 @@ class Service
|
||||
])
|
||||
->build();
|
||||
|
||||
if (
|
||||
if (
|
||||
(!$user->isPortal() || $user->isAdmin()) &&
|
||||
!$user->isApi()
|
||||
) {
|
||||
@@ -892,39 +887,24 @@ class Service
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{
|
||||
* offset?: int|null,
|
||||
* maxSize: int|null,
|
||||
* skipOwn?: bool,
|
||||
* where?: ?array<mixed,mixed>,
|
||||
* after?: ?string,
|
||||
* filter?: ?string,
|
||||
* } $params
|
||||
* @return array<mixed,mixed>
|
||||
*/
|
||||
private function getUserStreamWhereClause(array $params, User $user): array
|
||||
private function getUserStreamWhereClause(FindParams $params, User $user): array
|
||||
{
|
||||
$whereClause = [];
|
||||
|
||||
if (!empty($params['after'])) {
|
||||
$whereClause[]['createdAt>'] = $params['after'];
|
||||
if ($params->getAfter()) {
|
||||
$whereClause[]['createdAt>'] = $params->getAfter()->getString();
|
||||
}
|
||||
|
||||
if (!empty($params['filter'])) {
|
||||
switch ($params['filter']) {
|
||||
case 'posts':
|
||||
$whereClause[]['type'] = Note::TYPE_POST;
|
||||
|
||||
break;
|
||||
|
||||
case 'updates':
|
||||
$whereClause[]['type'] = [
|
||||
Note::TYPE_UPDATE,
|
||||
Note::TYPE_STATUS,
|
||||
];
|
||||
|
||||
break;
|
||||
}
|
||||
if ($params->getFilter() === FindParams::FILTER_POSTS) {
|
||||
$whereClause[]['type'] = Note::TYPE_POST;
|
||||
}
|
||||
else if ($params->getFilter() === FindParams::FILTER_UPDATES) {
|
||||
$whereClause[]['type'] = [
|
||||
Note::TYPE_UPDATE,
|
||||
Note::TYPE_STATUS,
|
||||
];
|
||||
}
|
||||
|
||||
return $whereClause;
|
||||
@@ -936,29 +916,17 @@ class Service
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{
|
||||
* offset?: int|null,
|
||||
* maxSize: int|null,
|
||||
* skipOwn?: bool,
|
||||
* where?: ?array<mixed,mixed>,
|
||||
* after?: ?string,
|
||||
* filter?: ?string,
|
||||
* } $params
|
||||
* @throws NotFound
|
||||
* @throws Forbidden
|
||||
* @return RecordCollection<Note>
|
||||
*/
|
||||
public function find(string $scope, ?string $id, array $params): RecordCollection
|
||||
public function find(string $scope, string $id, FindParams $params): RecordCollection
|
||||
{
|
||||
if ($scope === User::ENTITY_TYPE) {
|
||||
if (empty($id)) {
|
||||
$id = $this->user->getId();
|
||||
}
|
||||
|
||||
return $this->findUserStream($id, $params);
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$entity = $this->entityManager->getEntity($scope, $id);
|
||||
$entity = $this->entityManager->getEntityById($scope, $id);
|
||||
|
||||
$onlyTeamEntityTypeList = $this->getOnlyTeamEntityTypeList($this->user);
|
||||
$onlyOwnEntityTypeList = $this->getOnlyOwnEntityTypeList($this->user);
|
||||
@@ -973,11 +941,14 @@ class Service
|
||||
|
||||
$additionalQuery = null;
|
||||
|
||||
if (!empty($params['where'])) {
|
||||
$searchParams = SearchParams::fromRaw([
|
||||
'where' => $params['where'],
|
||||
]);
|
||||
$searchParams = $params->getSearchParams();
|
||||
|
||||
if (
|
||||
$searchParams->getWhere() ||
|
||||
$searchParams->getTextFilter() ||
|
||||
$searchParams->getPrimaryFilter() ||
|
||||
$searchParams->getBoolFilterList() !== []
|
||||
) {
|
||||
$additionalQuery = $this->selectBuilderFactory
|
||||
->create()
|
||||
->from(Note::ENTITY_TYPE)
|
||||
@@ -1116,21 +1087,16 @@ class Service
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($params['filter'])) {
|
||||
switch ($params['filter']) {
|
||||
case 'posts':
|
||||
$where['type'] = Note::TYPE_POST;
|
||||
$filter = $params->getFilter();
|
||||
|
||||
break;
|
||||
|
||||
case 'updates':
|
||||
$where['type'] = [
|
||||
Note::TYPE_ASSIGN,
|
||||
Note::TYPE_STATUS,
|
||||
];
|
||||
|
||||
break;
|
||||
}
|
||||
if ($filter === FindParams::FILTER_POSTS) {
|
||||
$where['type'] = Note::TYPE_POST;
|
||||
}
|
||||
else if ($filter === FindParams::FILTER_UPDATES) {
|
||||
$where['type'] = [
|
||||
Note::TYPE_ASSIGN,
|
||||
Note::TYPE_STATUS,
|
||||
];
|
||||
}
|
||||
|
||||
$ignoreScopeList = $this->getIgnoreScopeList($this->user);
|
||||
@@ -1162,16 +1128,20 @@ class Service
|
||||
|
||||
$builder->where($where);
|
||||
|
||||
if (!empty($params['after'])) {
|
||||
$after = $params->getAfter();
|
||||
$offset = $params->getOffset();
|
||||
$maxSize = $params->getMaxSize();
|
||||
|
||||
if ($after) {
|
||||
$builder->where([
|
||||
'createdAt>' => $params['after'],
|
||||
'createdAt>' => $after->getString(),
|
||||
]);
|
||||
}
|
||||
|
||||
$countBuilder = clone $builder;
|
||||
|
||||
$builder
|
||||
->limit($params['offset'] ?? 0, $params['maxSize'])
|
||||
->limit($offset ?? 0, $maxSize)
|
||||
->order('number', 'DESC');
|
||||
|
||||
$collection = $this->entityManager
|
||||
|
||||
Reference in New Issue
Block a user