1737 lines
44 KiB
PHP
1737 lines
44 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: 2021 F7cloud GmbH and F7cloud contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
|
|
namespace OCA\Circles\Db;
|
|
|
|
use Doctrine\DBAL\Query\QueryBuilder;
|
|
use OCA\Circles\Exceptions\RequestBuilderException;
|
|
use OCA\Circles\IFederatedModel;
|
|
use OCA\Circles\IFederatedUser;
|
|
use OCA\Circles\Model\Circle;
|
|
use OCA\Circles\Model\Federated\RemoteInstance;
|
|
use OCA\Circles\Model\FederatedUser;
|
|
use OCA\Circles\Model\Member;
|
|
use OCA\Circles\Model\Probes\CircleProbe;
|
|
use OCA\Circles\Service\ConfigService;
|
|
use OCA\Circles\Tools\Db\ExtendedQueryBuilder;
|
|
use OCA\Circles\Tools\Traits\TArrayTools;
|
|
use OCP\DB\QueryBuilder\ICompositeExpression;
|
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
|
use OCP\Server;
|
|
|
|
/**
|
|
* Class CoreQueryBuilder
|
|
*
|
|
* @package OCA\Circles\Db
|
|
*/
|
|
class CoreQueryBuilder extends ExtendedQueryBuilder {
|
|
use TArrayTools;
|
|
|
|
|
|
public const SINGLE = 'a';
|
|
public const CIRCLE = 'b';
|
|
public const MEMBER = 'c';
|
|
public const OWNER = 'd';
|
|
public const FEDERATED_EVENT = 'e';
|
|
public const REMOTE = 'f';
|
|
public const BASED_ON = 'g';
|
|
public const INITIATOR = 'h';
|
|
public const DIRECT_INITIATOR = 'i';
|
|
public const MEMBERSHIPS = 'j';
|
|
public const CONFIG = 'k';
|
|
public const UPSTREAM_MEMBERSHIPS = 'l';
|
|
public const INHERITANCE_FROM = 'm';
|
|
public const INHERITED_BY = 'n';
|
|
public const INVITED_BY = 'o';
|
|
public const MOUNT = 'p';
|
|
public const MOUNTPOINT = 'q';
|
|
public const SHARE = 'r';
|
|
public const FILE_CACHE = 's';
|
|
public const STORAGES = 't';
|
|
public const TOKEN = 'u';
|
|
public const OPTIONS = 'v';
|
|
public const HELPER = 'w';
|
|
|
|
|
|
public static $SQL_PATH = [
|
|
self::SINGLE => [
|
|
self::MEMBER
|
|
],
|
|
self::CIRCLE => [
|
|
self::OPTIONS => [
|
|
],
|
|
self::MEMBER,
|
|
self::OWNER => [
|
|
self::BASED_ON
|
|
],
|
|
self::MEMBERSHIPS => [
|
|
self::CONFIG
|
|
],
|
|
self::DIRECT_INITIATOR => [
|
|
self::BASED_ON
|
|
],
|
|
self::INITIATOR => [
|
|
self::BASED_ON,
|
|
self::INHERITED_BY => [
|
|
self::MEMBERSHIPS
|
|
]
|
|
],
|
|
self::REMOTE => [
|
|
self::MEMBER,
|
|
self::CIRCLE => [
|
|
self::OWNER
|
|
]
|
|
]
|
|
],
|
|
self::MEMBER => [
|
|
self::MEMBERSHIPS => [
|
|
self::CONFIG
|
|
],
|
|
self::INHERITANCE_FROM,
|
|
self::CIRCLE => [
|
|
self::OPTIONS => [
|
|
'getData' => true
|
|
],
|
|
self::OWNER,
|
|
self::MEMBERSHIPS => [
|
|
self::CONFIG
|
|
],
|
|
self::DIRECT_INITIATOR,
|
|
self::INITIATOR => [
|
|
self::OPTIONS => [
|
|
'minimumLevel' => Member::LEVEL_MEMBER
|
|
],
|
|
self::BASED_ON,
|
|
self::INHERITED_BY => [
|
|
self::MEMBERSHIPS
|
|
],
|
|
self::INVITED_BY => [
|
|
self::OWNER,
|
|
self::BASED_ON
|
|
]
|
|
]
|
|
],
|
|
self::BASED_ON => [
|
|
self::OWNER,
|
|
self::MEMBERSHIPS,
|
|
self::INITIATOR => [
|
|
self::BASED_ON,
|
|
self::INHERITED_BY => [
|
|
self::MEMBERSHIPS
|
|
]
|
|
]
|
|
],
|
|
self::REMOTE => [
|
|
self::MEMBER,
|
|
self::CIRCLE => [
|
|
self::OWNER
|
|
]
|
|
],
|
|
self::INVITED_BY => [
|
|
self::OWNER,
|
|
self::BASED_ON
|
|
]
|
|
],
|
|
self::MEMBERSHIPS => [
|
|
self::CONFIG
|
|
],
|
|
self::SHARE => [
|
|
self::SHARE,
|
|
self::TOKEN,
|
|
self::FILE_CACHE => [
|
|
self::STORAGES
|
|
],
|
|
self::UPSTREAM_MEMBERSHIPS => [
|
|
self::MEMBERSHIPS,
|
|
self::INHERITED_BY => [
|
|
self::BASED_ON
|
|
],
|
|
self::SHARE,
|
|
],
|
|
self::MEMBERSHIPS => [
|
|
self::CONFIG
|
|
],
|
|
self::INHERITANCE_FROM,
|
|
self::INHERITED_BY => [
|
|
self::BASED_ON
|
|
],
|
|
self::CIRCLE => [
|
|
self::OWNER
|
|
],
|
|
self::INITIATOR => [
|
|
self::BASED_ON,
|
|
self::INHERITED_BY => [
|
|
self::MEMBERSHIPS
|
|
]
|
|
]
|
|
],
|
|
self::REMOTE => [
|
|
self::MEMBER
|
|
],
|
|
self::MOUNT => [
|
|
self::MEMBER => [
|
|
self::REMOTE
|
|
],
|
|
self::INITIATOR => [
|
|
self::INHERITED_BY => [
|
|
self::MEMBERSHIPS
|
|
]
|
|
],
|
|
self::MOUNTPOINT,
|
|
self::MEMBERSHIPS => [
|
|
self::CONFIG
|
|
]
|
|
],
|
|
self::HELPER => [
|
|
self::MEMBERSHIPS => [
|
|
self::CONFIG
|
|
],
|
|
self::INITIATOR => [
|
|
self::INHERITED_BY => [
|
|
self::MEMBERSHIPS
|
|
]
|
|
],
|
|
self::CIRCLE => [
|
|
self::OPTIONS => [
|
|
],
|
|
self::MEMBER,
|
|
self::OWNER => [
|
|
self::BASED_ON
|
|
]
|
|
]
|
|
]
|
|
];
|
|
|
|
|
|
/** @var ConfigService */
|
|
private $configService;
|
|
|
|
private array $options = [];
|
|
private array $sqlPath = [];
|
|
|
|
/**
|
|
* CoreQueryBuilder constructor.
|
|
*/
|
|
public function __construct() {
|
|
parent::__construct();
|
|
|
|
$this->configService = Server::get(ConfigService::class);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param IFederatedModel $federatedModel
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getInstance(IFederatedModel $federatedModel): string {
|
|
$instance = $federatedModel->getInstance();
|
|
|
|
return ($this->configService->isLocalInstance($instance)) ? '' : $instance;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $id
|
|
*/
|
|
public function limitToCircleId(string $id): void {
|
|
$this->limit('circle_id', $id, '', true);
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
*/
|
|
public function limitToName(string $name): void {
|
|
$this->limit('name', $name);
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
*/
|
|
public function limitToDisplayName(string $name): void {
|
|
$this->limit('display_name', $name, '', false);
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
*/
|
|
public function limitToSanitizedName(string $name): void {
|
|
$this->limit('sanitized_name', $name, '', false);
|
|
}
|
|
|
|
/**
|
|
* @param int $config
|
|
*/
|
|
public function limitToConfig(int $config): void {
|
|
$this->limitInt('config', $config);
|
|
}
|
|
|
|
/**
|
|
* @param int $source
|
|
*/
|
|
public function limitToSource(int $source): void {
|
|
$this->limitInt('source', $source);
|
|
}
|
|
|
|
/**
|
|
* @param int $config
|
|
* @param string $alias
|
|
*/
|
|
public function limitToConfigFlag(int $config, string $alias = ''): void {
|
|
$this->limitBitwise('config', $config, $alias);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $singleId
|
|
*/
|
|
public function limitToSingleId(string $singleId, string $alias = ''): void {
|
|
$this->limit('single_id', $singleId, $alias, true);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $itemId
|
|
*/
|
|
public function limitToItemId(string $itemId): void {
|
|
$this->limit('item_id', $itemId, '', true);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $host
|
|
*/
|
|
public function limitToInstance(string $host): void {
|
|
$this->limit('instance', $host, '', false);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param int $userType
|
|
*/
|
|
public function limitToUserType(int $userType): void {
|
|
$this->limitInt('user_type', $userType);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param int $shareType
|
|
*/
|
|
public function limitToShareType(int $shareType): void {
|
|
$this->limitInt('share_type', $shareType);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $shareWith
|
|
*/
|
|
public function limitToShareWith(string $shareWith): void {
|
|
$this->limit('share_with', $shareWith, '', true);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param int $nodeId
|
|
*/
|
|
public function limitToFileSource(int $nodeId): void {
|
|
$this->limitInt('file_source', $nodeId);
|
|
}
|
|
|
|
/**
|
|
* @param array $files
|
|
*/
|
|
public function limitToFileSourceArray(array $files): void {
|
|
$this->limitInArray('file_source', $files, type: IQueryBuilder::PARAM_INT_ARRAY);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param int $shareId
|
|
*/
|
|
public function limitToShareParent(int $shareId): void {
|
|
$this->limitInt('parent', $shareId);
|
|
}
|
|
|
|
|
|
/**
|
|
* filter result on details (ie. displayName, Description, ...)
|
|
*
|
|
* @param Circle $circle
|
|
*/
|
|
public function filterCircleDetails(Circle $circle): void {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
$expr = $this->expr();
|
|
$andX = null;
|
|
if ($circle->getDisplayName() !== '') {
|
|
$andX = [];
|
|
foreach (explode(' ', $circle->getDisplayName()) as $word) {
|
|
$andX[] = $expr->iLike(
|
|
$this->getDefaultSelectAlias() . '.' . 'display_name',
|
|
$this->createNamedParameter('%' . $word . '%')
|
|
);
|
|
}
|
|
}
|
|
|
|
if ($andX !== null) {
|
|
$this->andWhere(...$andX);
|
|
}
|
|
|
|
if ($circle->getSource() > 0) {
|
|
$this->limitInt('source', $circle->getSource());
|
|
}
|
|
if ($circle->getConfig() > 0) {
|
|
$this->limitBitwise('config', $circle->getConfig());
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* left join RemoteInstance based on a Member
|
|
*/
|
|
public function leftJoinRemoteInstance(string $alias): void {
|
|
$expr = $this->expr();
|
|
|
|
try {
|
|
$aliasRemoteInstance = $this->generateAlias($alias, self::REMOTE);
|
|
$this->generateRemoteInstanceSelectAlias($aliasRemoteInstance)
|
|
->leftJoin(
|
|
$alias, CoreRequestBuilder::TABLE_REMOTE, $aliasRemoteInstance,
|
|
$expr->eq($alias . '.instance', $aliasRemoteInstance . '.instance')
|
|
);
|
|
} catch (RequestBuilderException $e) {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param RemoteInstance $remoteInstance
|
|
* @param bool $filterSensitiveData
|
|
* @param string $aliasCircle
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function limitToRemoteInstance(
|
|
string $alias,
|
|
RemoteInstance $remoteInstance,
|
|
bool $filterSensitiveData = true,
|
|
string $aliasCircle = '',
|
|
): void {
|
|
if ($aliasCircle === '') {
|
|
$aliasCircle = $alias;
|
|
}
|
|
|
|
$this->leftJoinRemoteInstanceIncomingRequest($alias, $remoteInstance);
|
|
$this->leftJoinMemberFromInstance($alias, $remoteInstance, $aliasCircle);
|
|
$this->leftJoinMemberFromRemoteCircle($alias, $remoteInstance, $aliasCircle);
|
|
$this->limitRemoteVisibility($alias, $filterSensitiveData, $aliasCircle);
|
|
}
|
|
|
|
|
|
/**
|
|
* Left join RemoteInstance based on an incoming request
|
|
*
|
|
* @param string $alias
|
|
* @param RemoteInstance $remoteInstance
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinRemoteInstanceIncomingRequest(
|
|
string $alias,
|
|
RemoteInstance $remoteInstance,
|
|
): void {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
$aliasRemote = $this->generateAlias($alias, self::REMOTE);
|
|
$expr = $this->expr();
|
|
$this->leftJoin(
|
|
$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_REMOTE, $aliasRemote,
|
|
$expr->eq($aliasRemote . '.instance', $this->createNamedParameter($remoteInstance->getInstance()))
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* left join members to check memberships of someone from instance
|
|
*
|
|
* @param string $alias
|
|
* @param RemoteInstance $remoteInstance
|
|
* @param string $aliasCircle
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
private function leftJoinMemberFromInstance(
|
|
string $alias, RemoteInstance $remoteInstance, string $aliasCircle,
|
|
) {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
$aliasRemote = $this->generateAlias($alias, self::REMOTE);
|
|
$aliasRemoteMember = $this->generateAlias($aliasRemote, self::MEMBER);
|
|
|
|
$expr = $this->expr();
|
|
$this->leftJoin(
|
|
$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasRemoteMember,
|
|
$expr->andX(
|
|
$expr->eq($aliasRemoteMember . '.circle_id', $aliasCircle . '.unique_id'),
|
|
$expr->eq(
|
|
$aliasRemoteMember . '.instance',
|
|
$this->createNamedParameter($remoteInstance->getInstance())
|
|
),
|
|
$expr->gte($aliasRemoteMember . '.level', $this->createNamedParameter(Member::LEVEL_MEMBER))
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* left join circle is member of a circle from remote instance
|
|
*
|
|
* @param string $alias
|
|
* @param RemoteInstance $remoteInstance
|
|
* @param string $aliasCircle
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
private function leftJoinMemberFromRemoteCircle(
|
|
string $alias,
|
|
RemoteInstance $remoteInstance,
|
|
string $aliasCircle,
|
|
) {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
$aliasRemote = $this->generateAlias($alias, self::REMOTE);
|
|
$aliasRemoteCircle = $this->generateAlias($aliasRemote, self::CIRCLE);
|
|
$aliasRemoteCircleOwner = $this->generateAlias($aliasRemoteCircle, self::OWNER);
|
|
|
|
$expr = $this->expr();
|
|
$this->leftJoin(
|
|
$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasRemoteCircle,
|
|
$expr->andX(
|
|
$expr->eq($aliasRemoteCircle . '.single_id', $aliasCircle . '.unique_id'),
|
|
$expr->emptyString($aliasRemoteCircle . '.instance'),
|
|
$expr->gte($aliasRemoteCircle . '.level', $this->createNamedParameter(Member::LEVEL_MEMBER))
|
|
)
|
|
);
|
|
$this->leftJoin(
|
|
$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasRemoteCircleOwner,
|
|
$expr->andX(
|
|
$expr->eq($aliasRemoteCircle . '.circle_id', $aliasRemoteCircleOwner . '.circle_id'),
|
|
$expr->eq(
|
|
$aliasRemoteCircleOwner . '.instance',
|
|
$this->createNamedParameter($remoteInstance->getInstance())
|
|
),
|
|
$expr->eq(
|
|
$aliasRemoteCircleOwner . '.level', $this->createNamedParameter(Member::LEVEL_OWNER)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* - global_scale: visibility on all Circles
|
|
* - trusted: visibility on all FEDERATED Circle if owner is local
|
|
* - external: visibility on all FEDERATED Circle if owner is local and:
|
|
* - with if Circle contains at least one member from the remote instance
|
|
* - one circle from the remote instance contains the local circle as member, and confirmed (using
|
|
* sync locally)
|
|
* - passive: like external, but the members list will only contains member from the local instance and
|
|
* from the remote instance.
|
|
*
|
|
* @param string $alias
|
|
* @param bool $sensitive
|
|
* @param string $aliasCircle
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
protected function limitRemoteVisibility(string $alias, bool $sensitive, string $aliasCircle) {
|
|
$aliasRemote = $this->generateAlias($alias, self::REMOTE);
|
|
$aliasOwner = $this->generateAlias($aliasCircle, self::OWNER);
|
|
$aliasRemoteMember = $this->generateAlias($aliasRemote, self::MEMBER);
|
|
$aliasRemoteCircle = $this->generateAlias($aliasRemote, self::CIRCLE);
|
|
$aliasRemoteCircleOwner = $this->generateAlias($aliasRemoteCircle, self::OWNER);
|
|
|
|
$expr = $this->expr();
|
|
$orX = [$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_GLOBALSCALE))];
|
|
|
|
$orExtOrPassive = [$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_EXTERNAL))];
|
|
if (!$sensitive) {
|
|
$orExtOrPassive[] = $expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_PASSIVE));
|
|
} else {
|
|
if ($this->getDefaultSelectAlias() === CoreQueryBuilder::MEMBER) {
|
|
$orExtOrPassive[] = $this->limitRemoteVisibility_Sensitive_Members($aliasRemote);
|
|
}
|
|
}
|
|
|
|
$orInstance = $expr->orX(
|
|
$expr->isNotNull($aliasRemoteMember . '.instance'),
|
|
$expr->isNotNull($aliasRemoteCircleOwner . '.instance'),
|
|
);
|
|
|
|
$andExternal = $expr->andX(
|
|
$expr->orX(...$orExtOrPassive),
|
|
$orInstance,
|
|
);
|
|
|
|
$orExtOrTrusted = $expr->orX(
|
|
$andExternal,
|
|
$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_TRUSTED)),
|
|
);
|
|
|
|
$orX[] = $expr->andX(
|
|
$orExtOrTrusted,
|
|
$this->exprLimitBitwise('config', Circle::CFG_FEDERATED, $aliasCircle),
|
|
$expr->emptyString($aliasOwner . '.instance'),
|
|
);
|
|
|
|
$this->andWhere($expr->orX(...$orX));
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param Member $member
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function limitToDirectMembership(string $alias, Member $member): void {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
$aliasMember = $this->generateAlias($alias, self::MEMBER, $options);
|
|
$getData = $this->getBool('getData', $options, false);
|
|
|
|
$expr = $this->expr();
|
|
if ($getData) {
|
|
$this->generateMemberSelectAlias($aliasMember);
|
|
}
|
|
$this->innerJoin(
|
|
$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
|
|
$expr->eq($aliasMember . '.circle_id', $alias . '.unique_id')
|
|
);
|
|
|
|
$this->filterDirectMembership($aliasMember, $member);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param string $aliasCircle
|
|
* @param FederatedUser $federatedUser
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function limitToFederatedUserMemberships(
|
|
string $alias,
|
|
string $aliasCircle,
|
|
FederatedUser $federatedUser,
|
|
): void {
|
|
$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS);
|
|
|
|
$this->leftJoin(
|
|
$aliasCircle,
|
|
'circles_membership',
|
|
$aliasMembership,
|
|
$this->expr()->eq($aliasCircle . '.unique_id', $aliasMembership . '.circle_id')
|
|
);
|
|
|
|
$this->andWhere(
|
|
$this->expr()->eq(
|
|
$aliasMembership . '.single_id',
|
|
$this->createNamedParameter($federatedUser->getSingleId())
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $aliasMember
|
|
* @param Member $member
|
|
*/
|
|
public function filterDirectMembership(string $aliasMember, Member $member): void {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
$expr = $this->expr();
|
|
$andX = [];
|
|
|
|
if ($member->getUserId() !== '') {
|
|
$andX[] = $expr->eq($aliasMember . '.user_id', $this->createNamedParameter($member->getUserId()));
|
|
}
|
|
|
|
if ($member->getSingleId() !== '') {
|
|
$andX[] = $expr->eq($aliasMember . '.single_id', $this->createNamedParameter($member->getSingleId()));
|
|
}
|
|
|
|
if ($member->getUserType() > 0) {
|
|
$andX[] = $expr->eq($aliasMember . '.user_type', $this->createNamedParameter($member->getUserType()));
|
|
}
|
|
|
|
$this->limitToInstance($this->getInstance($member));
|
|
|
|
if ($member->getLevel() > 0) {
|
|
$andX[] = $expr->gte(
|
|
$aliasMember . '.level',
|
|
$this->createNamedParameter($member->getLevel(), IQueryBuilder::PARAM_INT)
|
|
);
|
|
}
|
|
|
|
$this->andWhere($expr->andX(...$andX));
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param IFederatedUser|null $initiator
|
|
* @param string $field
|
|
* @param string $helperAlias
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinCircle(
|
|
string $alias,
|
|
?IFederatedUser $initiator = null,
|
|
string $field = 'circle_id',
|
|
string $helperAlias = '',
|
|
): void {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
$helperAlias = ($helperAlias !== '') ? $helperAlias : $alias;
|
|
$aliasCircle = $this->generateAlias($alias, self::CIRCLE, $options);
|
|
$getData = $this->getBool('getData', $options, false);
|
|
$expr = $this->expr();
|
|
|
|
if ($getData) {
|
|
$this->generateCircleSelectAlias($aliasCircle);
|
|
}
|
|
|
|
$this->leftJoin(
|
|
$helperAlias,
|
|
CoreRequestBuilder::TABLE_CIRCLE,
|
|
$aliasCircle,
|
|
$expr->eq($aliasCircle . '.unique_id', $helperAlias . '.' . $field)
|
|
);
|
|
|
|
if (!is_null($initiator)) {
|
|
$this->limitToInitiator($aliasCircle, $initiator);
|
|
}
|
|
|
|
$this->leftJoinOwner($aliasCircle);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $aliasMember
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinInvitedBy(string $aliasMember): void {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$aliasInvitedBy = $this->generateAlias($aliasMember, self::INVITED_BY);
|
|
} catch (RequestBuilderException $e) {
|
|
return;
|
|
}
|
|
|
|
$expr = $this->expr();
|
|
$this->generateCircleSelectAlias($aliasInvitedBy)
|
|
->leftJoin(
|
|
$aliasMember, CoreRequestBuilder::TABLE_CIRCLE, $aliasInvitedBy,
|
|
$expr->eq($aliasMember . '.invited_by', $aliasInvitedBy . '.unique_id')
|
|
);
|
|
|
|
$this->leftJoinOwner($aliasInvitedBy);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $aliasMember
|
|
* @param IFederatedUser|null $initiator
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinBasedOn(
|
|
string $aliasMember,
|
|
?IFederatedUser $initiator = null,
|
|
): void {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$aliasBasedOn = $this->generateAlias($aliasMember, self::BASED_ON, $options);
|
|
} catch (RequestBuilderException $e) {
|
|
return;
|
|
}
|
|
|
|
$expr = $this->expr();
|
|
$this->generateCircleSelectAlias($aliasBasedOn)
|
|
->leftJoin(
|
|
$aliasMember, CoreRequestBuilder::TABLE_CIRCLE, $aliasBasedOn,
|
|
$expr->eq($aliasBasedOn . '.unique_id', $aliasMember . '.single_id')
|
|
);
|
|
|
|
if (!is_null($initiator)) {
|
|
$this->leftJoinInitiator($aliasBasedOn, $initiator);
|
|
$this->leftJoinOwner($aliasBasedOn);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param string $field
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinOwner(string $alias, string $field = 'unique_id'): void {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$aliasMember = $this->generateAlias($alias, self::OWNER, $options);
|
|
$getData = $this->getBool('getData', $options, false);
|
|
} catch (RequestBuilderException $e) {
|
|
return;
|
|
}
|
|
|
|
$expr = $this->expr();
|
|
$this->generateMemberSelectAlias($aliasMember)
|
|
->leftJoin(
|
|
$alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
|
|
$expr->andX(
|
|
$expr->eq($aliasMember . '.circle_id', $alias . '.' . $field),
|
|
$expr->eq(
|
|
$aliasMember . '.level',
|
|
$this->createNamedParameter(Member::LEVEL_OWNER, self::PARAM_INT)
|
|
)
|
|
)
|
|
);
|
|
|
|
$this->leftJoinBasedOn($aliasMember);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param CircleProbe $probe
|
|
* @param string $alias
|
|
* @param string $field
|
|
*/
|
|
public function innerJoinMembership(
|
|
?CircleProbe $probe,
|
|
string $alias,
|
|
string $field = 'unique_id',
|
|
): void {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS);
|
|
} catch (RequestBuilderException $e) {
|
|
return;
|
|
}
|
|
|
|
$expr = $this->expr();
|
|
|
|
$on = $expr->andX($expr->eq($aliasMembership . '.circle_id', $alias . '.' . $field));
|
|
|
|
// limit on membership level if requested
|
|
$minLevel = $probe?->getMinimumLevel() ?? 0;
|
|
if ($minLevel > Member::LEVEL_MEMBER) {
|
|
$on->add($this->exprGt('level', $minLevel, true, $aliasMembership));
|
|
}
|
|
|
|
$this->generateMembershipSelectAlias($aliasMembership)
|
|
->innerJoin($alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasMembership, $on);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param string $fieldCircleId
|
|
* @param string $fieldSingleId
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinMember(
|
|
string $alias,
|
|
string $fieldCircleId = 'circle_id',
|
|
string $fieldSingleId = 'single_id',
|
|
): void {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$aliasMember = $this->generateAlias($alias, self::MEMBER, $options);
|
|
$getData = $this->getBool('getData', $options, false);
|
|
} catch (RequestBuilderException $e) {
|
|
return;
|
|
}
|
|
|
|
$expr = $this->expr();
|
|
$this->generateMemberSelectAlias($aliasMember)
|
|
->leftJoin(
|
|
$alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
|
|
$expr->andX(
|
|
$expr->eq($aliasMember . '.circle_id', $alias . '.' . $fieldCircleId),
|
|
$expr->eq($aliasMember . '.single_id', $alias . '.' . $fieldSingleId),
|
|
$this->exprGt('level', Member::LEVEL_MEMBER, true, $aliasMember)
|
|
)
|
|
);
|
|
|
|
$this->leftJoinRemoteInstance($aliasMember);
|
|
$this->leftJoinBasedOn($aliasMember);
|
|
}
|
|
|
|
|
|
/**
|
|
* if 'getData' is true, will returns 'inheritanceBy': the Member at the end of a sub-chain of
|
|
* memberships (based on $field for Top Circle's singleId)
|
|
*
|
|
* @param string $alias
|
|
* @param string $field
|
|
* @param string $aliasInheritedBy
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinInheritedMembers(
|
|
string $alias,
|
|
string $field = '',
|
|
string $aliasInheritedBy = '',
|
|
): void {
|
|
$expr = $this->expr();
|
|
|
|
$field = ($field === '') ? 'circle_id' : $field;
|
|
$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
|
|
|
|
$this->leftJoin(
|
|
$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasMembership,
|
|
$expr->eq($aliasMembership . '.circle_id', $alias . '.' . $field)
|
|
);
|
|
|
|
// if (!$this->getBool('getData', $options, false)) {
|
|
// return;
|
|
// }
|
|
|
|
if ($aliasInheritedBy === '') {
|
|
$aliasInheritedBy = $this->generateAlias($alias, self::INHERITED_BY);
|
|
}
|
|
$this->generateMemberSelectAlias($aliasInheritedBy)
|
|
->leftJoin(
|
|
$alias, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritedBy,
|
|
$expr->andX(
|
|
$expr->eq($aliasMembership . '.inheritance_last', $aliasInheritedBy . '.circle_id'),
|
|
$expr->eq($aliasMembership . '.single_id', $aliasInheritedBy . '.single_id')
|
|
)
|
|
);
|
|
|
|
$this->leftJoinBasedOn($aliasInheritedBy);
|
|
}
|
|
|
|
|
|
/**
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function limitToInheritedMemberships(string $alias, string $singleId, string $field = ''): void {
|
|
$expr = $this->expr();
|
|
$field = ($field === '') ? 'circle_id' : $field;
|
|
$aliasUpstreamMembership = $this->generateAlias($alias, self::UPSTREAM_MEMBERSHIPS, $options);
|
|
$this->leftJoin(
|
|
$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasUpstreamMembership,
|
|
$expr->eq($aliasUpstreamMembership . '.single_id', $this->createNamedParameter($singleId))
|
|
);
|
|
|
|
$orX = $expr->orX(
|
|
$expr->eq($aliasUpstreamMembership . '.circle_id', $alias . '.' . $field),
|
|
$expr->eq($alias . '.' . $field, $this->createNamedParameter($singleId))
|
|
);
|
|
|
|
$this->andWhere($orX);
|
|
}
|
|
|
|
|
|
/**
|
|
* limit the request to Members and Sub Members of a Circle.
|
|
*
|
|
* @param string $alias
|
|
* @param string $singleId
|
|
* @param int $level
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function limitToMembersByInheritance(string $alias, string $singleId, int $level = 0): void {
|
|
$this->leftJoinMembersByInheritance($alias);
|
|
|
|
$expr = $this->expr();
|
|
$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS);
|
|
$this->andWhere($expr->eq($aliasMembership . '.circle_id', $this->createNamedParameter($singleId)));
|
|
if ($level > 1) {
|
|
$this->andWhere(
|
|
$expr->gte(
|
|
$aliasMembership . '.level',
|
|
$this->createNamedParameter($level, IQueryBuilder::PARAM_INT)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* if 'getData' is true, will returns 'inheritanceFrom': the Circle-As-Member of the Top Circle
|
|
* that explain the membership of a Member (based on $field for singleId) to a specific Circle
|
|
*
|
|
* // TODO: returns the link/path ?
|
|
*
|
|
* @param string $alias
|
|
* @param string $field
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinMembersByInheritance(string $alias, string $field = ''): void {
|
|
$expr = $this->expr();
|
|
|
|
$field = ($field === '') ? 'circle_id' : $field;
|
|
$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
|
|
|
|
$this->leftJoin(
|
|
$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasMembership,
|
|
$expr->andX(
|
|
$expr->eq($aliasMembership . '.inheritance_last', $alias . '.' . $field),
|
|
$expr->eq($aliasMembership . '.single_id', $alias . '.single_id')
|
|
)
|
|
);
|
|
|
|
if (!$this->getBool('getData', $options, false)) {
|
|
return;
|
|
}
|
|
|
|
$aliasInheritanceFrom = $this->generateAlias($alias, self::INHERITANCE_FROM);
|
|
$this->generateMemberSelectAlias($aliasInheritanceFrom)
|
|
->leftJoin(
|
|
$aliasMembership, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritanceFrom,
|
|
$expr->andX(
|
|
$expr->eq($aliasMembership . '.circle_id', $aliasInheritanceFrom . '.circle_id'),
|
|
$expr->eq($aliasMembership . '.inheritance_first', $aliasInheritanceFrom . '.single_id')
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param string $token
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function limitToShareToken(string $alias, string $token): void {
|
|
$this->leftJoinShareToken($alias);
|
|
|
|
$aliasShareToken = $this->generateAlias($alias, self::TOKEN, $options);
|
|
$this->generateSelectAlias(
|
|
CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_TOKEN],
|
|
$aliasShareToken,
|
|
$aliasShareToken,
|
|
[]
|
|
);
|
|
|
|
$this->limit('token', $token, $aliasShareToken);
|
|
}
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param string $field
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinShareToken(string $alias, string $field = ''): void {
|
|
$expr = $this->expr();
|
|
|
|
$field = ($field === '') ? 'id' : $field;
|
|
$aliasShareToken = $this->generateAlias($alias, self::TOKEN, $options);
|
|
|
|
$this->leftJoin(
|
|
$alias, CoreRequestBuilder::TABLE_TOKEN, $aliasShareToken,
|
|
$expr->eq($aliasShareToken . '.share_id', $alias . '.' . $field)
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* limit the result to the point of view of a FederatedUser
|
|
*
|
|
* @param string $alias
|
|
* @param IFederatedUser $user
|
|
* @param string $field
|
|
* @param string $helperAlias
|
|
*
|
|
* @return ICompositeExpression
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function limitToInitiator(
|
|
string $alias,
|
|
IFederatedUser $user,
|
|
string $field = '',
|
|
string $helperAlias = '',
|
|
): ICompositeExpression {
|
|
$this->leftJoinInitiator($alias, $user, $field, $helperAlias);
|
|
$where = $this->limitInitiatorVisibility($alias);
|
|
|
|
$aliasInitiator = $this->generateAlias($alias, self::INITIATOR, $options);
|
|
if ($this->getBool('getData', $options, false)) {
|
|
$this->leftJoinBasedOn($aliasInitiator);
|
|
}
|
|
|
|
return $where;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $alias
|
|
*/
|
|
public function leftJoinCircleConfig(string $alias): void {
|
|
$expr = $this->expr();
|
|
try {
|
|
$aliasConfig = $this->generateAlias($alias, self::CONFIG, $options);
|
|
$this->selectAlias(
|
|
$aliasConfig . '.config',
|
|
(($alias !== $this->getDefaultSelectAlias()) ? $alias . '_' : '') . 'circle_config'
|
|
);
|
|
$this->leftJoin(
|
|
$alias,
|
|
CoreRequestBuilder::TABLE_CIRCLE,
|
|
$aliasConfig,
|
|
$expr->eq($alias . '.circle_id', $aliasConfig . '.unique_id')
|
|
);
|
|
} catch (RequestBuilderException $e) {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Left join members to filter userId as initiator.
|
|
*
|
|
* @param string $alias
|
|
* @param IFederatedUser $initiator
|
|
* @param string $field
|
|
* @param string $helperAlias
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinInitiator(
|
|
string $alias,
|
|
IFederatedUser $initiator,
|
|
string $field = '',
|
|
string $helperAlias = '',
|
|
): void {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
$expr = $this->expr();
|
|
$field = ($field === '') ? 'unique_id' : $field;
|
|
$helperAlias = ($helperAlias !== '') ? $helperAlias : $alias;
|
|
$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
|
|
|
|
$this->leftJoin(
|
|
$helperAlias,
|
|
CoreRequestBuilder::TABLE_MEMBERSHIP,
|
|
$aliasMembership,
|
|
$expr->andX(
|
|
$this->exprLimit('single_id', $initiator->getSingleId(), $aliasMembership),
|
|
$expr->eq($aliasMembership . '.circle_id', $helperAlias . '.' . $field)
|
|
)
|
|
);
|
|
|
|
|
|
$listMembershipCircleAlias = [$aliasMembership];
|
|
if ($this->getBool('initiatorDirectMember', $options, false)) {
|
|
try {
|
|
$aliasDirectInitiator = $this->generateAlias($alias, self::DIRECT_INITIATOR, $options);
|
|
$listMembershipCircleAlias[] = $aliasDirectInitiator;
|
|
} catch (RequestBuilderException $e) {
|
|
// meaning that this path does not require DIRECT_INITIATOR; can be safely ignored
|
|
}
|
|
}
|
|
|
|
try {
|
|
$aliasMembershipCircle = $this->generateAlias($aliasMembership, self::CONFIG, $options);
|
|
$orXMembershipCircle = $expr->orX(...array_map(
|
|
function (string $alias) use ($aliasMembershipCircle) {
|
|
return $this->expr()->eq(
|
|
$alias . '.circle_id',
|
|
$aliasMembershipCircle . '.unique_id'
|
|
);
|
|
},
|
|
$listMembershipCircleAlias
|
|
));
|
|
|
|
$this->leftJoin(
|
|
$aliasMembership,
|
|
CoreRequestBuilder::TABLE_CIRCLE,
|
|
$aliasMembershipCircle,
|
|
$orXMembershipCircle
|
|
);
|
|
} catch (RequestBuilderException $e) {
|
|
// meaning that this path (ie. self::$SQL_PATH) does not require CONFIG; can be safely ignored
|
|
}
|
|
|
|
if (!$this->getBool('getData', $options, false)) {
|
|
return;
|
|
}
|
|
|
|
// bypass memberships
|
|
if ($this->getBool('initiatorDirectMember', $options, false)) {
|
|
try {
|
|
$aliasDirectInitiator = $this->generateAlias($alias, self::DIRECT_INITIATOR, $options);
|
|
$this->generateMemberSelectAlias($aliasDirectInitiator)
|
|
->leftJoin(
|
|
$helperAlias,
|
|
CoreRequestBuilder::TABLE_MEMBER,
|
|
$aliasDirectInitiator,
|
|
$expr->andX(
|
|
$this->exprLimit('single_id', $initiator->getSingleId(), $aliasDirectInitiator),
|
|
$expr->eq($aliasDirectInitiator . '.circle_id', $helperAlias . '.' . $field)
|
|
)
|
|
);
|
|
} catch (RequestBuilderException $e) {
|
|
// meaning that this path does not require DIRECT_INITIATOR; can be safely ignored
|
|
}
|
|
}
|
|
|
|
|
|
try {
|
|
$aliasInitiator = $this->generateAlias($alias, self::INITIATOR, $options);
|
|
$this->leftJoin(
|
|
$aliasMembership, CoreRequestBuilder::TABLE_MEMBER, $aliasInitiator,
|
|
$expr->andX(
|
|
$expr->eq($aliasMembership . '.inheritance_first', $aliasInitiator . '.single_id'),
|
|
$expr->eq($aliasMembership . '.circle_id', $aliasInitiator . '.circle_id')
|
|
)
|
|
);
|
|
|
|
$aliasInheritedBy = $this->generateAlias($aliasInitiator, self::INHERITED_BY);
|
|
$this->leftJoin(
|
|
$aliasInitiator, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritedBy,
|
|
$expr->andX(
|
|
$expr->eq($aliasMembership . '.single_id', $aliasInheritedBy . '.single_id'),
|
|
$expr->eq($aliasMembership . '.inheritance_last', $aliasInheritedBy . '.circle_id')
|
|
)
|
|
);
|
|
|
|
$default = [];
|
|
if ($this->getBool('emulateVisitor', $options)) {
|
|
$default = [
|
|
'user_id' => $initiator->getUserId(),
|
|
'single_id' => $initiator->getSingleId(),
|
|
'user_type' => $initiator->getUserType(),
|
|
'cached_name' => $initiator->getDisplayName(),
|
|
'instance' => $initiator->getInstance()
|
|
];
|
|
}
|
|
$aliasInheritedByMembership = $this->generateAlias($aliasInheritedBy, self::MEMBERSHIPS);
|
|
$this->generateMemberSelectAlias($aliasInitiator, $default)
|
|
->generateMemberSelectAlias($aliasInheritedBy)
|
|
->generateMembershipSelectAlias($aliasMembership, $aliasInheritedByMembership);
|
|
} catch (RequestBuilderException $e) {
|
|
}
|
|
}
|
|
|
|
|
|
public function completeProbeWithInitiator(
|
|
string $alias,
|
|
string $field = 'single_id',
|
|
string $helperAlias = '',
|
|
): void {
|
|
if ($this->getType() !== QueryBuilder::SELECT) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$aliasInitiator = $this->generateAlias($alias, self::INITIATOR);
|
|
} catch (RequestBuilderException $e) {
|
|
return;
|
|
}
|
|
|
|
$helperAlias = ($helperAlias === '') ? $alias : $helperAlias;
|
|
|
|
$expr = $this->expr();
|
|
$this->generateMemberSelectAlias($aliasInitiator)
|
|
->leftJoin(
|
|
$alias, CoreRequestBuilder::TABLE_MEMBER, $aliasInitiator,
|
|
$expr->andX(
|
|
$expr->eq($aliasInitiator . '.circle_id', $alias . '.unique_id'),
|
|
$expr->eq($aliasInitiator . '.' . $field, $helperAlias . '.inheritance_first'),
|
|
)
|
|
);
|
|
//
|
|
// $this->leftJoinBasedOn($aliasInitiator);
|
|
}
|
|
|
|
/**
|
|
* @param string $alias
|
|
*
|
|
* @return ICompositeExpression
|
|
* @throws RequestBuilderException
|
|
*/
|
|
protected function limitInitiatorVisibility(string $alias): ICompositeExpression {
|
|
$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
|
|
$aliasMembershipCircle = $this->generateAlias($aliasMembership, self::CONFIG, $options);
|
|
$levelCheck = [$aliasMembership];
|
|
|
|
$directMember = '';
|
|
if ($this->getBool('initiatorDirectMember', $options, false)) {
|
|
$directMember = $this->generateAlias($alias, self::DIRECT_INITIATOR, $options);
|
|
array_push($levelCheck, $directMember);
|
|
}
|
|
|
|
$expr = $this->expr();
|
|
|
|
// Visibility to non-member is
|
|
// - 0 (default), if initiator is member
|
|
// - 2 (Personal), if initiator is owner)
|
|
// - 4 (Visible to everyone)
|
|
$orX = [];
|
|
|
|
// filterPersonalCircles will remove access to Personal Circles as Owner
|
|
if (!$this->getBool('filterPersonalCircles', $options, false)) {
|
|
$orX[] = $expr->andX(
|
|
$this->exprLimitBitwise('config', Circle::CFG_PERSONAL, $aliasMembershipCircle),
|
|
$expr->eq($aliasMembership . '.level', $this->createNamedParameter(Member::LEVEL_OWNER))
|
|
);
|
|
}
|
|
|
|
$minimumLevel = $this->getInt('minimumLevel', $options);
|
|
$orXLevelCheck = $expr->orX(...array_map(
|
|
function (string $alias) use ($minimumLevel) {
|
|
return $this->expr()->gte(
|
|
$alias . '.level',
|
|
$this->createNamedParameter($minimumLevel, self::PARAM_INT)
|
|
);
|
|
},
|
|
$levelCheck
|
|
));
|
|
$andXMember = [$orXLevelCheck];
|
|
|
|
if (!$this->getBool('includePersonalCircles', $options, false)) {
|
|
$andXMember[] = $this->exprFilterBitwise(
|
|
'config', Circle::CFG_PERSONAL,
|
|
$aliasMembershipCircle
|
|
);
|
|
}
|
|
$orX[] = $expr->andX(...$andXMember);
|
|
|
|
if ($directMember !== '' && $this->getBool('allowRequestingMembership', $options, false)) {
|
|
$orX[] = $expr->orX(
|
|
$this->exprLimit('status', Member::STATUS_REQUEST, $directMember),
|
|
$this->exprLimit('status', Member::STATUS_INVITED, $directMember)
|
|
);
|
|
}
|
|
|
|
if ($minimumLevel === 0 && $alias === self::CIRCLE) {
|
|
$orX[] = $this->exprLimitBitwise('config', Circle::CFG_VISIBLE, $alias);
|
|
}
|
|
|
|
if ($this->getBool('includeNonVisibleCircles', $options)) {
|
|
$orX[] = $expr->andX(
|
|
$this->exprLimitBitwise('config', Circle::CFG_OPEN, $alias),
|
|
$this->exprFilterBitwise('config', Circle::CFG_VISIBLE, $alias),
|
|
);
|
|
}
|
|
|
|
if ($this->getBool('visitingSingleCircles', $options, false)) {
|
|
$orX[] = $this->exprLimitBitwise('config', Circle::CFG_SINGLE, $alias);
|
|
}
|
|
|
|
// if Member can be Visitor, we only filter access to Personal Circles
|
|
if ($this->getBool('viewableThroughKeyhole', $options, false)) {
|
|
$andOpen = [
|
|
$this->exprLimitBitwise('config', Circle::CFG_OPEN, $alias),
|
|
$this->exprLimitBitwise('config', Circle::CFG_VISIBLE, $alias),
|
|
];
|
|
if (!$this->configService->getAppValueBool(ConfigService::KEYHOLE_CFG_REQUEST)) {
|
|
$andOpen[] = $this->exprFilterBitwise('config', Circle::CFG_REQUEST, $alias);
|
|
}
|
|
$orX[] = $expr->andX(...$andOpen);
|
|
}
|
|
|
|
$orX = $expr->orX(...$orX);
|
|
|
|
$this->andWhere($orX);
|
|
|
|
return $orX;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $aliasCircle
|
|
* @param CircleProbe $probe
|
|
*/
|
|
public function filterCircles(string $aliasCircle, CircleProbe $probe): void {
|
|
$filter = $probe->filtered();
|
|
$include = $probe->included();
|
|
$config = ($filter | $include) - $include;
|
|
$this->filterBitwise('config', $config, $aliasCircle);
|
|
}
|
|
|
|
|
|
/**
|
|
* Limit visibility on Sensitive information when search for members.
|
|
*
|
|
* @param string $alias
|
|
*
|
|
* @return ICompositeExpression
|
|
*/
|
|
private function limitRemoteVisibility_Sensitive_Members(string $alias): ICompositeExpression {
|
|
$expr = $this->expr();
|
|
return $expr->andX(
|
|
$expr->eq($alias . '.type', $this->createNamedParameter(RemoteInstance::TYPE_PASSIVE)),
|
|
$expr->orX(
|
|
$expr->eq($this->getDefaultSelectAlias() . '.instance', $alias . '.instance'),
|
|
// TODO: do we need this ? (display members from the local instance)
|
|
$expr->emptyString($this->getDefaultSelectAlias() . '.instance'),
|
|
$expr->eq(
|
|
$this->getDefaultSelectAlias() . '.level',
|
|
$this->createNamedParameter(Member::LEVEL_OWNER)
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Link to storage/filecache
|
|
*
|
|
* @param string $aliasShare
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinFileCache(string $aliasShare) {
|
|
$expr = $this->expr();
|
|
|
|
$aliasFileCache = $this->generateAlias($aliasShare, self::FILE_CACHE);
|
|
$aliasStorages = $this->generateAlias($aliasFileCache, self::STORAGES);
|
|
|
|
$this->generateSelectAlias(
|
|
CoreRequestBuilder::$outsideTables[CoreRequestBuilder::TABLE_FILE_CACHE],
|
|
$aliasFileCache,
|
|
$aliasFileCache,
|
|
[]
|
|
)
|
|
->generateSelectAlias(
|
|
CoreRequestBuilder::$outsideTables[CoreRequestBuilder::TABLE_STORAGES],
|
|
$aliasStorages,
|
|
$aliasStorages,
|
|
[]
|
|
)
|
|
->leftJoin(
|
|
$aliasShare, CoreRequestBuilder::TABLE_FILE_CACHE, $aliasFileCache,
|
|
$expr->eq($aliasShare . '.file_source', $aliasFileCache . '.fileid')
|
|
)
|
|
->leftJoin(
|
|
$aliasFileCache, CoreRequestBuilder::TABLE_STORAGES, $aliasStorages,
|
|
$expr->eq($aliasFileCache . '.storage', $aliasStorages . '.numeric_id')
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $aliasShare
|
|
* @param string $aliasShareMemberships
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinShareChild(string $aliasShare, string $aliasShareMemberships = '') {
|
|
$expr = $this->expr();
|
|
|
|
$aliasShareChild = $this->generateAlias($aliasShare, self::SHARE);
|
|
if ($aliasShareMemberships === '') {
|
|
$aliasShareMemberships = $this->generateAlias($aliasShare, self::MEMBERSHIPS, $options);
|
|
}
|
|
|
|
$this->leftJoin(
|
|
$aliasShareMemberships, CoreRequestBuilder::TABLE_SHARE, $aliasShareChild,
|
|
$expr->andX(
|
|
$expr->eq($aliasShareChild . '.parent', $aliasShare . '.id'),
|
|
$expr->eq($aliasShareChild . '.share_with', $aliasShareMemberships . '.single_id')
|
|
)
|
|
);
|
|
|
|
$this->generateSelectAlias(
|
|
['id', 'file_target', 'permissions'],
|
|
$aliasShareChild,
|
|
'child',
|
|
[]
|
|
);
|
|
|
|
// $this->selectAlias($aliasShareParent . '.permissions', 'parent_perms');
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param FederatedUser $federatedUser
|
|
* @param bool $reshares
|
|
*/
|
|
public function limitToShareOwner(
|
|
string $alias,
|
|
FederatedUser $federatedUser,
|
|
bool $reshares,
|
|
int $nodeId = 0,
|
|
): void {
|
|
$expr = $this->expr();
|
|
|
|
if ($reshares === false) {
|
|
$this->andWhere($this->exprLimit('uid_initiator', $federatedUser->getUserId(), $alias));
|
|
} elseif ($nodeId === 0) {
|
|
$this->andWhere(
|
|
$expr->orX(
|
|
$this->exprLimit('uid_owner', $federatedUser->getUserId(), $alias),
|
|
$this->exprLimit('uid_initiator', $federatedUser->getUserId(), $alias)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $aliasMount
|
|
* @param string $aliasMountMemberships
|
|
*
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function leftJoinMountpoint(string $aliasMount, IFederatedUser $federatedUser, string $aliasMountMemberships = '') {
|
|
$expr = $this->expr();
|
|
|
|
$aliasMountpoint = $this->generateAlias($aliasMount, self::MOUNTPOINT);
|
|
if ($aliasMountMemberships === '') {
|
|
$aliasMountMemberships = $this->generateAlias($aliasMount, self::MEMBERSHIPS, $options);
|
|
}
|
|
|
|
$this->leftJoin(
|
|
$aliasMountMemberships, CoreRequestBuilder::TABLE_MOUNTPOINT, $aliasMountpoint,
|
|
$expr->andX(
|
|
$expr->eq($aliasMountpoint . '.mount_id', $aliasMount . '.mount_id'),
|
|
$expr->eq($aliasMountpoint . '.single_id', $this->createNamedParameter($federatedUser->getSingleId()))
|
|
)
|
|
);
|
|
|
|
$this->selectAlias($aliasMountpoint . '.mountpoint', $aliasMountpoint . '_mountpoint');
|
|
$this->selectAlias($aliasMountpoint . '.mountpoint_hash', $aliasMountpoint . '_mountpoint_hash');
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param array $default
|
|
*
|
|
* @return CoreQueryBuilder&IQueryBuilder
|
|
*/
|
|
private function generateCircleSelectAlias(string $alias, array $default = []): self {
|
|
$this->generateSelectAlias(
|
|
CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_CIRCLE],
|
|
$alias,
|
|
$alias,
|
|
$default
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param array $default
|
|
*
|
|
* @return $this
|
|
*/
|
|
private function generateMemberSelectAlias(string $alias, array $default = []): self {
|
|
$this->generateSelectAlias(
|
|
CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_MEMBER],
|
|
$alias,
|
|
$alias,
|
|
$default
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param array $default
|
|
* @param string $prefix
|
|
*
|
|
* @return $this
|
|
*/
|
|
private function generateMembershipSelectAlias(
|
|
string $alias,
|
|
string $prefix = '',
|
|
array $default = [],
|
|
): self {
|
|
$this->generateSelectAlias(
|
|
CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_MEMBERSHIP],
|
|
$alias,
|
|
($prefix === '') ? $alias : $prefix,
|
|
$default
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param array $default
|
|
*
|
|
* @return $this
|
|
*/
|
|
private function generateRemoteInstanceSelectAlias(string $alias, array $default = []): self {
|
|
$this->generateSelectAlias(
|
|
CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_REMOTE],
|
|
$alias,
|
|
$alias,
|
|
$default
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param array $path
|
|
* @param array $options
|
|
*
|
|
* @return CoreQueryBuilder&IQueryBuilder
|
|
*/
|
|
public function setOptions(array $path, array $options): self {
|
|
$options = [self::OPTIONS => $options];
|
|
foreach (array_reverse($path) as $item) {
|
|
$options = [$item => $options];
|
|
}
|
|
|
|
$this->options = array_merge_recursive($this->options, $options);
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $base
|
|
* @param string $extension
|
|
* @param array|null $options
|
|
*
|
|
* @return string
|
|
* @throws RequestBuilderException
|
|
*/
|
|
public function generateAlias(string $base, string $extension, ?array &$options = []): string {
|
|
$search = str_replace('_', '.', $base);
|
|
$path = $search . '.' . $extension;
|
|
if (!$this->validKey($path, $this->getSqlPath())
|
|
&& !in_array($extension, $this->getArray($search, $this->getSqlPath()))) {
|
|
throw new RequestBuilderException($extension . ' not found in ' . $search);
|
|
}
|
|
|
|
if (!is_array($options)) {
|
|
$options = [];
|
|
}
|
|
|
|
$optionPath = '';
|
|
foreach (explode('.', $path) as $p) {
|
|
$optionPath = trim($optionPath . '.' . $p, '.');
|
|
$options = array_merge(
|
|
$options,
|
|
$this->getArray($optionPath . '.' . self::OPTIONS, $this->getSqlPath()),
|
|
$this->getArray($optionPath . '.' . self::OPTIONS, $this->options)
|
|
);
|
|
}
|
|
|
|
return $base . '_' . $extension;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $prefix
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getAvailablePath(string $prefix): array {
|
|
$prefix = trim($prefix, '_');
|
|
$search = str_replace('_', '.', $prefix);
|
|
|
|
$path = [];
|
|
foreach ($this->getArray($search, $this->getSqlPath()) as $arr => $item) {
|
|
if (is_numeric($arr)) {
|
|
$k = $item;
|
|
} else {
|
|
$k = $arr;
|
|
}
|
|
$path[$k] = $prefix . '_' . $k . '_';
|
|
}
|
|
|
|
return $path;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getSqlPath(): array {
|
|
if (empty($this->sqlPath)) {
|
|
return self::$SQL_PATH;
|
|
}
|
|
|
|
return $this->sqlPath;
|
|
}
|
|
|
|
|
|
/**
|
|
* DataProbe uses this to set which data need to be extracted, based on self::$SQL_PATH.
|
|
*
|
|
* @param string $key
|
|
* @param array $path
|
|
*
|
|
* @return $this
|
|
*/
|
|
public function setSqlPath(string $key, array $path = []): self {
|
|
if (empty($this->sqlPath)) {
|
|
$this->sqlPath = self::$SQL_PATH;
|
|
}
|
|
|
|
$this->sqlPath[$key] = $path;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function resetSqlPath(): self {
|
|
$this->sqlPath = [];
|
|
|
|
return $this;
|
|
}
|
|
}
|