migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
@@ -0,0 +1,74 @@
<?php
namespace OCA\Circles\Activity;
use OCA\Circles\AppInfo\Application;
use OCP\Activity\IFilter;
use OCP\IL10N;
use OCP\IURLGenerator;
class Filter implements IFilter {
/** @var IL10N */
protected $l10n;
/** @var IURLGenerator */
protected $url;
public function __construct(IL10N $l10n, IURLGenerator $url) {
$this->l10n = $l10n;
$this->url = $url;
}
/**
* @return string Lowercase a-z only identifier
* @since 11.0.0
*/
public function getIdentifier() {
return Application::APP_ID;
}
/**
* @return string A translated string
* @since 11.0.0
*/
public function getName() {
return $this->l10n->t('Circles');
}
/**
* @return int
* @since 11.0.0
*/
public function getPriority() {
return 10;
}
/**
* @return string Full URL to an icon, empty string when none is given
* @since 11.0.0
*/
public function getIcon() {
return $this->url->getAbsoluteURL(
$this->url->imagePath(Application::APP_ID, 'circles.svg')
);
}
/**
* @param string[] $types
*
* @return string[] An array of allowed apps from which activities should be displayed
* @since 11.0.0
*/
public function filterTypes(array $types) {
return $types;
}
/**
* @return string[] An array of allowed apps from which activities should be displayed
* @since 11.0.0
*/
public function allowedApps() {
return [Application::APP_ID];
}
}
@@ -0,0 +1,275 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Activity;
use Exception;
use InvalidArgumentException;
use OCA\Circles\AppInfo\Application;
use OCA\Circles\Exceptions\FakeException;
use OCA\Circles\Model\DeprecatedCircle;
use OCA\Circles\Model\FederatedLink;
use OCA\Circles\Model\DeprecatedMember;
use OCA\Circles\Service\CirclesService;
use OCA\Circles\Service\MiscService;
use OCP\Activity\IEvent;
use OCP\Activity\IManager;
use OCP\Activity\IProvider;
/**
* Class Provider
*
* @package OCA\Circles\Activity
*/
class Provider implements IProvider {
/** @var ProviderSubjectCircle */
private $parserCircle;
/** @var ProviderSubjectMember */
private $parserMember;
/** @var ProviderSubjectGroup */
private $parserGroup;
/** @var ProviderSubjectLink */
private $parserLink;
/** @var MiscService */
protected $miscService;
/** @var IManager */
protected $activityManager;
public function __construct(
IManager $activityManager, MiscService $miscService, ProviderSubjectCircle $parserCircle,
ProviderSubjectMember $parserMember, ProviderSubjectGroup $parserGroup,
ProviderSubjectLink $parserLink
) {
$this->activityManager = $activityManager;
$this->miscService = $miscService;
$this->parserCircle = $parserCircle;
$this->parserMember = $parserMember;
$this->parserGroup = $parserGroup;
$this->parserLink = $parserLink;
}
/**
* {@inheritdoc}
*/
public function parse($lang, IEvent $event, IEvent $previousEvent = null) {
try {
$params = $event->getSubjectParameters();
$this->initActivityParser($event, $params);
$circle = DeprecatedCircle::fromJSON($params['circle']);
$this->setIcon($event, $circle);
$this->parseAsNonMember($event, $circle, $params);
$this->parseAsMember($event, $circle, $params);
$this->parseAsModerator($event, $circle, $params);
} catch (FakeException $e) {
/** clean exit */
}
return $event;
}
/**
* @param IEvent $event
* @param array $params
*/
private function initActivityParser(IEvent $event, $params) {
if ($event->getApp() !== Application::APP_ID) {
throw new InvalidArgumentException();
}
if (!key_exists('circle', $params)) {
throw new InvalidArgumentException();
}
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
*/
private function setIcon(IEvent $event, DeprecatedCircle $circle) {
$event->setIcon(
CirclesService::getCircleIcon(
$circle->getType(),
(method_exists($this->activityManager, 'getRequirePNG')
&& $this->activityManager->getRequirePNG())
)
);
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param array $params
*
* @throws FakeException
*/
private function parseAsNonMember(IEvent $event, DeprecatedCircle $circle, $params) {
if ($event->getType() !== 'circles_as_non_member') {
return;
}
$this->parserCircle->parseSubjectCircleCreate($event, $circle);
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param array $params
*
* @throws FakeException
*/
private function parseAsMember(IEvent $event, DeprecatedCircle $circle, $params) {
if ($event->getType() !== 'circles_as_member') {
return;
}
// $this->parserCircle->parseSubjectCircleCreate($event, $circle);
$this->parserCircle->parseSubjectCircleDelete($event, $circle);
$this->parseMemberAsMember($event, $circle, $params);
}
/**
* @param DeprecatedCircle $circle
* @param IEvent $event
* @param array $params
*
* @throws Exception
*/
private function parseAsModerator(IEvent $event, DeprecatedCircle $circle, $params) {
if ($event->getType() !== 'circles_as_moderator') {
return;
}
$this->parseMemberAsModerator($event, $circle, $params);
$this->parseGroupAsModerator($event, $circle, $params);
$this->parseLinkAsModerator($event, $circle, $params);
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param array $params
*
* @throws FakeException
*/
private function parseMemberAsMember(IEvent $event, DeprecatedCircle $circle, $params) {
if (!key_exists('member', $params)) {
return;
}
$member = DeprecatedMember::fromJSON($params['member']);
$this->parserMember->parseSubjectMemberJoin($event, $circle, $member);
$this->parserMember->parseSubjectMemberAdd($event, $circle, $member);
$this->parserMember->parseSubjectMemberLeft($event, $circle, $member);
$this->parserMember->parseSubjectMemberRemove($event, $circle, $member);
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param array $params
*
* @throws FakeException
*/
private function parseGroupAsModerator(IEvent $event, DeprecatedCircle $circle, $params) {
if (!key_exists('group', $params)) {
return;
}
$group = DeprecatedMember::fromJSON($params['group']);
$this->parserGroup->parseGroupLink($event, $circle, $group);
$this->parserGroup->parseGroupUnlink($event, $circle, $group);
$this->parserGroup->parseGroupLevel($event, $circle, $group);
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param array $params
*
* @throws FakeException
*/
private function parseMemberAsModerator(IEvent $event, DeprecatedCircle $circle, $params) {
if (!key_exists('member', $params)) {
return;
}
$member = DeprecatedMember::fromJSON($params['member']);
$this->parserMember->parseMemberInvited($event, $circle, $member);
$this->parserMember->parseMemberLevel($event, $circle, $member);
$this->parserMember->parseMemberRequestInvitation($event, $circle, $member);
$this->parserMember->parseMemberOwner($event, $circle, $member);
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param array $params
*
* @throws FakeException
*/
private function parseLinkAsModerator(IEvent $event, DeprecatedCircle $circle, $params) {
if (!key_exists('link', $params)) {
return;
}
$remote = FederatedLink::fromJSON($params['link']);
$this->parserLink->parseLinkRequestSent($event, $circle, $remote);
$this->parserLink->parseLinkRequestReceived($event, $circle, $remote);
$this->parserLink->parseLinkRequestRejected($event, $circle, $remote);
$this->parserLink->parseLinkRequestCanceled($event, $circle, $remote);
$this->parserLink->parseLinkRequestAccepted($event, $circle, $remote);
$this->parserLink->parseLinkRequestRemoved($event, $circle, $remote);
$this->parserLink->parseLinkRequestCanceling($event, $circle, $remote);
$this->parserLink->parseLinkRequestAccepting($event, $circle, $remote);
$this->parserLink->parseLinkUp($event, $circle, $remote);
$this->parserLink->parseLinkDown($event, $circle, $remote);
$this->parserLink->parseLinkRemove($event, $circle, $remote);
}
}
@@ -0,0 +1,376 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Activity;
use OCA\Circles\Api\v1\Circles;
use OCA\Circles\Model\DeprecatedCircle;
use OCA\Circles\Model\FederatedLink;
use OCA\Circles\Model\DeprecatedMember;
use OCA\Circles\Service\MiscService;
use OCP\Activity\IEvent;
use OCP\Activity\IManager;
use OCP\IL10N;
use OCP\IURLGenerator;
class ProviderParser {
/** @var MiscService */
protected $miscService;
/** @var IL10N */
protected $l10n;
/** @var IURLGenerator */
protected $url;
/** @var IManager */
protected $activityManager;
public function __construct(
IURLGenerator $url, IManager $activityManager, IL10N $l10n, MiscService $miscService
) {
$this->url = $url;
$this->activityManager = $activityManager;
$this->l10n = $l10n;
$this->miscService = $miscService;
}
/**
* general function to generate Circle event.
*
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param FederatedLink|null $remote
* @param string $ownEvent
* @param string $othersEvent
*/
protected function parseCircleEvent(IEvent $event, DeprecatedCircle $circle, $remote, $ownEvent, $othersEvent
) {
$data = [
'author' => $this->generateViewerParameter($circle),
'circle' => $this->generateCircleParameter($circle)
];
$remoteCircle = $this->generateRemoteCircleParameter($remote);
if ($remoteCircle !== null) {
$data['remote'] = $remoteCircle;
}
if ($this->isViewerTheAuthor($circle, $this->activityManager->getCurrentUserId())) {
$this->setSubject($event, $ownEvent, $data);
return;
}
$this->setSubject($event, $othersEvent, $data);
}
/**
* @param IEvent $event
* @param string $line
* @param array $data
*/
protected function setSubject(IEvent $event, $line, $data) {
$this->setParsedSubject($event, $line, $data);
$this->setRichSubject($event, $line, $data);
}
/**
* @param IEvent $event
* @param string $line
* @param array $data
*/
protected function setRichSubject(IEvent $event, $line, $data) {
$ak = array_keys($data);
foreach ($ak as $k) {
$subAk = array_keys($data[$k]);
foreach ($subAk as $sK) {
if (substr($sK, 0, 1) === '_') {
unset($data[$k][$sK]);
}
}
}
$event->setRichSubject($line, $data);
}
/**
* @param IEvent $event
* @param string $line
* @param array $data
*/
protected function setParsedSubject(IEvent $event, $line, $data) {
$ak = array_keys($data);
$replace = [];
foreach ($ak as $k) {
if (!key_exists('_parsed', $data[$k])) {
continue;
}
$replace['{' . $k . '}'] = $data[$k]['_parsed'];
}
$line = strtr($line, $replace);
$event->setParsedSubject($line);
}
/**
* general function to generate Member event.
*
* @param DeprecatedCircle $circle
* @param $member
* @param IEvent $event
* @param $ownEvent
* @param $othersEvent
*/
protected function parseMemberEvent(
IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member, $ownEvent, $othersEvent
) {
$data = [
'circle' => $this->generateCircleParameter($circle),
'member' => $this->generateUserParameter($member)
];
if ($member->getUserId() === $this->activityManager->getCurrentUserId()
) {
$this->setSubject($event, $ownEvent, $data);
return;
}
$this->setSubject($event, $othersEvent, $data);
}
/**
* general function to generate Link event.
*
* @param DeprecatedCircle $circle
* @param FederatedLink $remote
* @param IEvent $event
* @param string $line
*/
protected function parseLinkEvent(IEvent $event, DeprecatedCircle $circle, FederatedLink $remote, $line
) {
$data = [
'circle' => $this->generateCircleParameter($circle),
'remote' => $this->generateRemoteCircleParameter($remote)
];
$this->setSubject($event, $line, $data);
}
/**
* general function to generate Circle+Member event.
*
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
* @param IEvent $event
* @param string $ownEvent
* @param string $othersEvent
*/
protected function parseCircleMemberEvent(
IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member, $ownEvent, $othersEvent
) {
$data = [
'author' => $this->generateViewerParameter($circle),
'circle' => $this->generateCircleParameter($circle),
'member' => $this->generateUserParameter($member),
'external' => $this->generateExternalMemberParameter($member),
'group' => $this->generateGroupParameter($member),
];
if ($this->isViewerTheAuthor($circle, $this->activityManager->getCurrentUserId())) {
$this->setSubject($event, $ownEvent, $data);
return;
}
$this->setSubject($event, $othersEvent, $data);
}
/**
* general function to generate Circle+Member advanced event.
*
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
* @param IEvent $event
* @param $ownEvent
* @param $targetEvent
* @param $othersEvent
*/
protected function parseCircleMemberAdvancedEvent(
IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member, $ownEvent, $targetEvent, $othersEvent
) {
$data = [
'author' => $this->generateViewerParameter($circle),
'circle' => $this->generateCircleParameter($circle),
'member' => $this->generateUserParameter($member)
];
if ($this->isViewerTheAuthor($circle, $this->activityManager->getCurrentUserId())) {
$this->setSubject($event, $ownEvent, $data);
return;
}
if ($member->getUserId() === $this->activityManager->getCurrentUserId()) {
$this->setSubject($event, $targetEvent, $data);
return;
}
$this->setSubject($event, $othersEvent, $data);
}
/**
* @param DeprecatedCircle $circle
* @param string $userId
*
* @return bool
*/
protected function isViewerTheAuthor(DeprecatedCircle $circle, $userId) {
if ($circle->getViewer() === null) {
return false;
}
if ($circle->getViewer()
->getUserId() === $userId) {
return true;
}
return false;
}
/**
* @param DeprecatedCircle $circle
*
* @return string|array <string,string|integer>
*/
protected function generateViewerParameter(DeprecatedCircle $circle) {
if ($circle->getViewer() === null) {
return [];
}
return $this->generateUserParameter($circle->getViewer());
}
/**
* @param DeprecatedMember $member
*
* @return array|string <string,string|integer>
*/
protected function generateExternalMemberParameter(DeprecatedMember $member) {
return [
'type' => $member->getTypeName(),
'id' => $member->getUserId(),
'name' => $member->getCachedName() . ' (' . $member->getTypeString() . ')',
'_parsed' => $member->getCachedName()
];
}
/**
* @param DeprecatedCircle $circle
*
* @return array<string,string|integer>
*/
protected function generateCircleParameter(DeprecatedCircle $circle) {
return [
'type' => 'circle',
'id' => $circle->getId(),
'name' => $circle->getName(),
'_parsed' => $circle->getName(),
'link' => Circles::generateAbsoluteLink($circle->getUniqueId())
];
}
/**
* @param FederatedLink $link
*
* @return array<string,string|integer>
*/
protected function generateRemoteCircleParameter($link) {
if ($link === null) {
return null;
}
return [
'type' => 'circle',
'id' => $link->getUniqueId(),
'name' => $link->getToken() . '@' . $link->getAddress(),
'_parsed' => $link->getToken() . '@' . $link->getAddress()
];
}
/**
* @param DeprecatedMember $member
*
* @return array <string,string|integer>
*/
protected function generateUserParameter(DeprecatedMember $member) {
$display = $member->getCachedName();
if ($display === '') {
$display = $member->getUserId();
}
return [
'type' => 'user',
'id' => $member->getUserId(),
'name' => $display,
'_parsed' => $display
];
}
/**
* @param DeprecatedMember $group
*
* @return array <string,string|integer>
*/
protected function generateGroupParameter($group) {
return [
'type' => 'user-group',
'id' => $group->getUserId(),
'name' => $group->getUserId(),
'_parsed' => $group->getUserId()
];
}
}
@@ -0,0 +1,75 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Activity;
use OCA\Circles\Exceptions\FakeException;
use OCA\Circles\Model\DeprecatedCircle;
use OCP\Activity\IEvent;
class ProviderSubjectCircle extends ProviderParser {
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
*
* @throws FakeException
*/
public function parseSubjectCircleCreate(IEvent $event, DeprecatedCircle $circle) {
if ($event->getSubject() !== 'circle_create') {
return;
}
$this->parseCircleEvent(
$event, $circle, null,
$this->l10n->t('You created the circle {circle}'),
$this->l10n->t('{author} created the circle {circle}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
*
* @throws FakeException
*/
public function parseSubjectCircleDelete(IEvent $event, DeprecatedCircle $circle) {
if ($event->getSubject() !== 'circle_delete') {
return;
}
$this->parseCircleEvent(
$event, $circle, null,
$this->l10n->t('You deleted {circle}'),
$this->l10n->t('{author} deleted {circle}')
);
throw new FakeException();
}
}
@@ -0,0 +1,101 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Activity;
use OCA\Circles\Exceptions\FakeException;
use OCA\Circles\Model\DeprecatedCircle;
use OCA\Circles\Model\DeprecatedMember;
use OCP\Activity\IEvent;
class ProviderSubjectGroup extends ProviderParser {
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $group
*
* @throws FakeException
*/
public function parseGroupLink(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $group) {
if ($event->getSubject() !== 'group_link') {
return;
}
$this->parseCircleMemberEvent(
$event, $circle, $group,
$this->l10n->t('You linked {group} to {circle}'),
$this->l10n->t('{group} has been linked to {circle} by {author}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $group
*
* @throws FakeException
*/
public function parseGroupUnlink(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $group) {
if ($event->getSubject() !== 'group_unlink') {
return;
}
$this->parseCircleMemberEvent(
$event, $circle, $group,
$this->l10n->t('You unlinked {group} from {circle}'),
$this->l10n->t('{group} has been unlinked from {circle} by {author}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $group
*
* @throws FakeException
*/
public function parseGroupLevel(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $group) {
if ($event->getSubject() !== 'group_level') {
return;
}
$l = $this->l10n;
$level = [$l->t($group->getLevelString())];
$this->parseCircleMemberEvent(
$event, $circle, $group,
$l->t('You changed the level of the linked group {group} in {circle} to %1$s', $level),
$l->t('{author} changed the level of the linked group {group} in {circle} to %1$s', $level)
);
throw new FakeException();
}
}
@@ -0,0 +1,273 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Activity;
use OCA\Circles\Exceptions\FakeException;
use OCA\Circles\Model\DeprecatedCircle;
use OCA\Circles\Model\FederatedLink;
use OCP\Activity\IEvent;
class ProviderSubjectLink extends ProviderParser {
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param FederatedLink $remote
*
* @throws FakeException
*/
public function parseLinkRequestSent(IEvent $event, DeprecatedCircle $circle, FederatedLink $remote) {
if ($event->getSubject() !== 'link_request_sent') {
return;
}
$this->parseCircleEvent(
$event, $circle, $remote,
$this->l10n->t('You sent a request to link {circle} with {remote}'),
$this->l10n->t('{author} sent a request to link {circle} with {remote}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param FederatedLink $remote
*
* @throws FakeException
*/
public function parseLinkRequestReceived(IEvent $event, DeprecatedCircle $circle, FederatedLink $remote) {
if ($event->getSubject() !== 'link_request_received') {
return;
}
$this->parseLinkEvent(
$event, $circle, $remote, $this->l10n->t('{remote} requested a link with {circle}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param FederatedLink $remote
*
* @throws FakeException
*/
public function parseLinkRequestRejected(IEvent $event, DeprecatedCircle $circle, FederatedLink $remote) {
if ($event->getSubject() !== 'link_request_rejected') {
return;
}
$this->parseLinkEvent(
$event, $circle, $remote,
$this->l10n->t('The request to link {circle} with {remote} has been rejected')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param FederatedLink $remote
*
* @throws FakeException
*/
public function parseLinkRequestCanceled(IEvent $event, DeprecatedCircle $circle, FederatedLink $remote) {
if ($event->getSubject() !== 'link_request_canceled') {
return;
}
$this->parseLinkEvent(
$event, $circle, $remote,
$this->l10n->t(
'The request to link {remote} with {circle} has been canceled remotely'
)
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param FederatedLink $remote
*
* @throws FakeException
*/
public function parseLinkRequestAccepted(IEvent $event, DeprecatedCircle $circle, FederatedLink $remote) {
if ($event->getSubject() !== 'link_request_accepted') {
return;
}
$this->parseLinkEvent(
$event, $circle, $remote,
$this->l10n->t('The request to link {circle} with {remote} has been accepted')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param FederatedLink $remote
*
* @throws FakeException
*/
public function parseLinkRequestRemoved(IEvent $event, DeprecatedCircle $circle, FederatedLink $remote) {
if ($event->getSubject() !== 'link_request_removed') {
return;
}
$this->parseCircleEvent(
$event, $circle, $remote,
$this->l10n->t('You dismissed the request to link {remote} with {circle}'),
$this->l10n->t('{author} dismissed the request to link {remote} with {circle}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param FederatedLink $remote
*
* @throws FakeException
*/
public function parseLinkRequestCanceling(IEvent $event, DeprecatedCircle $circle, FederatedLink $remote) {
if ($event->getSubject() !== 'link_request_canceling') {
return;
}
$this->parseCircleEvent(
$event, $circle, $remote,
$this->l10n->t('You canceled the request to link {circle} with {remote}'),
$this->l10n->t('{author} canceled the request to link {circle} with {remote}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param FederatedLink $remote
*
* @throws FakeException
*/
public function parseLinkRequestAccepting(IEvent $event, DeprecatedCircle $circle, FederatedLink $remote) {
if ($event->getSubject() !== 'link_request_accepting') {
return;
}
$this->parseCircleEvent(
$event, $circle, $remote,
$this->l10n->t('You accepted the request to link {remote} with {circle}'),
$this->l10n->t('{author} accepted the request to link {remote} with {circle}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param FederatedLink $remote
*
* @throws FakeException
*/
public function parseLinkUp(IEvent $event, DeprecatedCircle $circle, FederatedLink $remote) {
if ($event->getSubject() !== 'link_up') {
return;
}
$this->parseLinkEvent(
$event, $circle, $remote,
$this->l10n->t('A link between {circle} and {remote} is now up and running')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param FederatedLink $remote
*
* @throws FakeException
*/
public function parseLinkDown(IEvent $event, DeprecatedCircle $circle, FederatedLink $remote) {
if ($event->getSubject() !== 'link_down') {
return;
}
$this->parseLinkEvent(
$event, $circle, $remote,
$this->l10n->t(
'The link between {circle} and {remote} has been shutdown remotely'
)
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param FederatedLink $remote
*
* @throws FakeException
*/
public function parseLinkRemove(IEvent $event, DeprecatedCircle $circle, FederatedLink $remote) {
if ($event->getSubject() !== 'link_remove') {
return;
}
$this->parseCircleEvent(
$event, $circle, $remote,
$this->l10n->t('You closed the link between {circle} and {remote}'),
$this->l10n->t('{author} closed the link between {circle} and {remote}')
);
throw new FakeException();
}
}
@@ -0,0 +1,410 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Activity;
use OCA\Circles\Exceptions\FakeException;
use OCA\Circles\Model\DeprecatedCircle;
use OCA\Circles\Model\DeprecatedMember;
use OCP\Activity\IEvent;
class ProviderSubjectMember extends ProviderParser {
/**
* Parse on Subject 'member_join'.
* If circle is closed, we say that user accepted his invitation.
*
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
public function parseSubjectMemberJoin(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member) {
if ($event->getSubject() !== 'member_join') {
return;
}
$this->parseSubjectMemberJoinClosedCircle($event, $circle, $member);
$this->parseCircleMemberEvent(
$event, $circle, $member, $this->l10n->t('You joined {circle}'),
$this->l10n->t('{member} joined {circle}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
private function parseSubjectMemberJoinClosedCircle(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member) {
if ($circle->getType() !== DeprecatedCircle::CIRCLES_CLOSED) {
return;
}
$this->parseCircleMemberEvent(
$event, $circle, $member,
$this->l10n->t('You accepted the invitation to join {circle}'),
$this->l10n->t('{member} accepted the invitation to join {circle}')
);
throw new FakeException();
}
/**
* Parse on Subject 'member_add'.
* If circle is closed, we say that user's invitation was accepted.
*
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
public function parseSubjectMemberAdd(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member) {
if ($event->getSubject() !== 'member_add') {
return;
}
$this->parseSubjectMemberAddNotLocalMember($event, $circle, $member);
$this->parseSubjectMemberAddClosedCircle($event, $circle, $member);
$this->parseCircleMemberAdvancedEvent(
$event, $circle, $member,
$this->l10n->t('You added {member} as member to {circle}'),
$this->l10n->t('You have been added as member to {circle} by {author}'),
$this->l10n->t('{member} has been added as member to {circle} by {author}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
private function parseSubjectMemberAddNotLocalMember(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member
) {
if ($member->getType() === DeprecatedMember::TYPE_USER) {
return;
}
$this->parseCircleMemberEvent(
$event, $circle, $member,
$this->l10n->t('You added {external} to {circle}'),
$this->l10n->t('{external} has been added to {circle} by {author}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
private function parseSubjectMemberAddClosedCircle(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member) {
if ($circle->getType() !== DeprecatedCircle::CIRCLES_CLOSED) {
return;
}
$this->parseCircleMemberAdvancedEvent(
$event, $circle, $member,
$this->l10n->t("You accepted {member}'s request to join {circle}"),
$this->l10n->t('Your request to join {circle} has been accepted by {author}'),
$this->l10n->t("{member}'s request to join {circle} has been accepted by {author}")
);
throw new FakeException();
}
/**
* Parse on Subject 'member_left'.
* If circle is closed and member was not a real member, we send him to
* parseSubjectNonMemberLeftClosedCircle();
*
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
public function parseSubjectMemberLeft(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member) {
if ($event->getSubject() !== 'member_left') {
return;
}
$this->parseSubjectNonMemberLeftClosedCircle($event, $circle, $member);
$this->parseCircleMemberEvent(
$event, $circle, $member,
$this->l10n->t('You left {circle}'),
$this->l10n->t('{member} left {circle}')
);
throw new FakeException();
}
/**
* Parse on Subject 'member_left' on a closed circle when user were not yet a member.
* If status is Invited we say that member rejected his invitation.
* If status is Requested we say he dismissed his request.
*
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
private function parseSubjectNonMemberLeftClosedCircle(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member
) {
if ($circle->getType() !== DeprecatedCircle::CIRCLES_CLOSED
|| $member->getLevel() !== DeprecatedMember::LEVEL_NONE) {
return;
}
if ($member->getStatus() === DeprecatedMember::STATUS_INVITED) {
$this->parseCircleMemberEvent(
$event, $circle, $member,
$this->l10n->t("You declined the invitation to join {circle}"),
$this->l10n->t("{member} declined an invitation to join {circle}")
);
} else {
$this->parseCircleMemberEvent(
$event, $circle, $member,
$this->l10n->t("You cancelled your request to join {circle}"),
$this->l10n->t("{member} cancelled his request to join {circle}")
);
}
throw new FakeException();
}
/**
* Parse on Subject 'member_remove'.
* If circle is closed and member was not a real member, we send him to
* parseSubjectNonMemberRemoveClosedCircle();
*
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
public function parseSubjectMemberRemove(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member) {
if ($event->getSubject() !== 'member_remove') {
return;
}
$this->parseSubjectMemberRemoveNotLocalMember($event, $circle, $member);
$this->parseSubjectMemberRemoveNotYetMember($event, $circle, $member);
$this->parseCircleMemberAdvancedEvent(
$event, $circle, $member,
$this->l10n->t('You removed {member} from {circle}'),
$this->l10n->t('You have been removed from {circle} by {author}'),
$this->l10n->t('{member} has been removed from {circle} by {author}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
private function parseSubjectMemberRemoveNotLocalMember(
IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member
) {
if ($member->getType() === DeprecatedMember::TYPE_USER) {
return;
}
$this->parseCircleMemberEvent(
$event, $circle, $member,
$this->l10n->t('You removed {external} from {circle}'),
$this->l10n->t('{external} has been removed from {circle} by {author}')
);
throw new FakeException();
}
/**
* Parse on Subject 'member_remove' on a closed circle when user were not yet a member.
* If status is Invited we say that author cancelled his invitation.
* If status is Requested we say that his invitation was rejected.
*
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
private function parseSubjectMemberRemoveNotYetMember(
IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member
) {
if ($circle->getType() !== DeprecatedCircle::CIRCLES_CLOSED
|| $member->getLevel() !== DeprecatedMember::LEVEL_NONE) {
return;
}
$this->parseSubjectMemberRemoveNotYetMemberRequesting($event, $circle, $member);
$this->parseCircleMemberAdvancedEvent(
$event, $circle, $member,
$this->l10n->t("You cancelled {member}'s invitation to join {circle}"),
$this->l10n->t('Your invitation to join {circle} has been cancelled by {author}'),
$this->l10n->t("{author} cancelled {member}'s invitation to join {circle}")
);
throw new FakeException();
}
private function parseSubjectMemberRemoveNotYetMemberRequesting(
IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member
) {
if ($member->getStatus() !== DeprecatedMember::STATUS_REQUEST) {
return;
}
$this->parseCircleMemberAdvancedEvent(
$event, $circle, $member,
$this->l10n->t("You dismissed {member}'s request to join {circle}"),
$this->l10n->t('Your request to join {circle} has been dismissed by {author}'),
$this->l10n->t("{member}'s request to join {circle} has been dismissed by {author}")
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
public function parseMemberInvited(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member) {
if ($event->getSubject() !== 'member_invited') {
return;
}
$this->parseCircleMemberAdvancedEvent(
$event, $circle, $member,
$this->l10n->t('You invited {member} to join {circle}'),
$this->l10n->t('You have been invited to join {circle} by {author}'),
$this->l10n->t('{member} has been invited to join {circle} by {author}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
public function parseMemberLevel(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member) {
if ($event->getSubject() !== 'member_level') {
return;
}
$level = [$this->l10n->t($member->getLevelString())];
$this->parseCircleMemberAdvancedEvent(
$event, $circle, $member,
$this->l10n->t('You changed {member}\'s level in {circle} to %1$s', $level),
$this->l10n->t('{author} changed your level in {circle} to %1$s', $level),
$this->l10n->t('{author} changed {member}\'s level in {circle} to %1$s', $level)
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
public function parseMemberRequestInvitation(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member) {
if ($event->getSubject() !== 'member_request_invitation') {
return;
}
$this->parseMemberEvent(
$event, $circle, $member,
$this->l10n->t('You sent a request to join {circle}'),
$this->l10n->t('{member} sent a request to join {circle}')
);
throw new FakeException();
}
/**
* @param IEvent $event
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*
* @throws FakeException
*/
public function parseMemberOwner(IEvent $event, DeprecatedCircle $circle, DeprecatedMember $member) {
if ($event->getSubject() !== 'member_owner') {
return;
}
$this->parseMemberEvent(
$event, $circle, $member,
$this->l10n->t('You are the new owner of {circle}'),
$this->l10n->t('{member} is the new owner of {circle}')
);
throw new FakeException();
}
}
@@ -0,0 +1,84 @@
<?php
namespace OCA\Circles\Activity;
use OCP\Activity\ISetting;
use OCP\IL10N;
class SettingAsMember implements ISetting {
/** @var IL10N */
protected $l10n;
/**
* @param IL10N $l10n
*/
public function __construct(IL10N $l10n) {
$this->l10n = $l10n;
}
/**
* @return string Lowercase a-z and underscore only identifier
* @since 11.0.0
*/
public function getIdentifier() {
return 'circles_as_member';
}
/**
* @return string A translated string
* @since 11.0.0
*/
public function getName() {
return $this->l10n->t('On events happening in a <strong>Circle</strong> of which you are a member');
}
/**
* @return int whether the filter should be rather on the top or bottom of
* the admin section. The filters are arranged in ascending order of the
* priority values. It is required to return a value between 0 and 100.
* @since 11.0.0
*/
public function getPriority() {
return 60;
}
/**
* @return bool True when the option can be changed for the stream
* @since 11.0.0
*/
public function canChangeStream() {
return true;
}
/**
* @return bool True when the option can be changed for the stream
* @since 11.0.0
*/
public function isDefaultEnabledStream() {
return true;
}
/**
* @return bool True when the option can be changed for the mail
* @since 11.0.0
*/
public function canChangeMail() {
return true;
}
/**
* @return bool True when the option can be changed for the stream
* @since 11.0.0
*/
public function isDefaultEnabledMail() {
return false;
}
}
@@ -0,0 +1,76 @@
<?php
namespace OCA\Circles\Activity;
use OCP\Activity\ISetting;
use OCP\IL10N;
class SettingAsModerator implements ISetting {
/** @var IL10N */
protected $l10n;
/**
* @param IL10N $l10n
*/
public function __construct(IL10N $l10n) {
$this->l10n = $l10n;
}
/**
* @return string Lowercase a-z and underscore only identifier
* @since 11.0.0
*/
public function getIdentifier() {
return 'circles_as_moderator';
}
/**
* @return string A translated string
* @since 11.0.0
*/
public function getName() {
return $this->l10n->t('Any important event in a <strong>Circle</strong> you are moderating');
}
/**
* @return int whether the filter should be rather on the top or bottom of
* the admin section. The filters are arranged in ascending order of the
* priority values. It is required to return a value between 0 and 100.
* @since 11.0.0
*/
public function getPriority() {
return 60;
}
/**
* @return bool True when the option can be changed for the stream
* @since 11.0.0
*/
public function canChangeStream() {
return true;
}
/**
* @return bool True when the option can be changed for the stream
* @since 11.0.0
*/
public function isDefaultEnabledStream() {
return true;
}
/**
* @return bool True when the option can be changed for the mail
* @since 11.0.0
*/
public function canChangeMail() {
return true;
}
/**
* @return bool True when the option can be changed for the stream
* @since 11.0.0
*/
public function isDefaultEnabledMail() {
return true;
}
}
@@ -0,0 +1,89 @@
<?php
namespace OCA\Circles\Activity;
use OCP\Activity\ISetting;
use OCP\IL10N;
/**
* Class SettingAsNonMember
*
* @package OCA\Circles\Activity
*/
class SettingAsNonMember implements ISetting {
/** @var IL10N */
protected $l10n;
/**
* @param IL10N $l10n
*/
public function __construct(IL10N $l10n) {
$this->l10n = $l10n;
}
/**
* @return string Lowercase a-z and underscore only identifier
* @since 11.0.0
*/
public function getIdentifier() {
return 'circles_as_non_member';
}
/**
* @return string A translated string
* @since 11.0.0
*/
public function getName() {
return $this->l10n->t('On global events happening in any <strong>Circle</strong>');
}
/**
* @return int whether the filter should be rather on the top or bottom of
* the admin section. The filters are arranged in ascending order of the
* priority values. It is required to return a value between 0 and 100.
* @since 11.0.0
*/
public function getPriority() {
return 60;
}
/**
* @return bool True when the option can be changed for the stream
* @since 11.0.0
*/
public function canChangeStream() {
return true;
}
/**
* @return bool True when the option can be changed for the stream
* @since 11.0.0
*/
public function isDefaultEnabledStream() {
return false;
}
/**
* @return bool True when the option can be changed for the mail
* @since 11.0.0
*/
public function canChangeMail() {
return true;
}
/**
* @return bool True when the option can be changed for the stream
* @since 11.0.0
*/
public function isDefaultEnabledMail() {
return false;
}
}
@@ -0,0 +1,268 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @author Vinicius Cubas Brand <vinicius@eita.org.br>
* @author Daniel Tygel <dtygel@eita.org.br>
*
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Api\v1;
use OCA\Circles\CirclesManager;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\Membership;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Model\ShareWrapper;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\ShareWrapperService;
class Circles {
public const API_VERSION = [0, 10, 0];
// Expose circle and member constants via API
public const CIRCLES_PERSONAL = 1;
public const CIRCLES_SECRET = 2;
public const CIRCLES_CLOSED = 4;
public const CIRCLES_PUBLIC = 8;
public const CIRCLES_ALL = 15;
public const TYPE_USER = Member::TYPE_USER;
public const TYPE_GROUP = Member::TYPE_GROUP;
public const TYPE_MAIL = Member::TYPE_MAIL;
public const TYPE_CONTACT = Member::TYPE_CONTACT;
public const LEVEL_NONE = Member::LEVEL_NONE;
public const LEVEL_MEMBER = Member::LEVEL_MEMBER;
public const LEVEL_MODERATOR = Member::LEVEL_MODERATOR;
public const LEVEL_ADMIN = Member::LEVEL_ADMIN;
public const LEVEL_OWNER = Member::LEVEL_OWNER;
/**
* Circles::listCircles();
*
* This function list all circles fitting a search regarding its name and the level and the
* rights from the current user. In case of Secret circle, name needs to be complete so the
* circle is included in the list (or if the current user is the owner)
*
* example: Circles::listCircles(Circles::CIRCLES_ALL, '', 8, callback); will returns all
* circles when the current user is at least an Admin.
*
* @param mixed $type
* @param string $name
* @param int $level
* @param string $userId
* @param bool $forceAll
*
* @return Circle[]
*/
public static function listCircles($type, $name = '', $level = 0, $userId = '', $forceAll = false) {
/** @var FederatedUserService $federatedUserService */
$federatedUserService = \OC::$server->get(FederatedUserService::class);
$personalCircle = false;
if ($forceAll) {
$personalCircle = true;
}
if ($userId === '') {
$federatedUserService->initCurrentUser();
} else {
$federatedUserService->setLocalCurrentUserId($userId);
}
/** @var CircleService $circleService */
$circleService = \OC::$server->get(CircleService::class);
$probe = new CircleProbe();
$probe->includePersonalCircles($personalCircle);
$probe->filterHiddenCircles();
return $circleService->getCircles($probe);
}
/**
* @param string $userId
* @param bool $forceAll
*
* @return Circle[]
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InitiatorNotFoundException
* @throws InvalidIdException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
*
* @deprecated - used by apps/dav/lib/Connector/Sabre/Principal.php
*
* Circles::joinedCircles();
*
* Return all the circle the current user is a member.
*/
public static function joinedCircles($userId = '', $forceAll = false) {
/** @var FederatedUserService $federatedUserService */
$federatedUserService = \OC::$server->get(FederatedUserService::class);
$personalCircle = false;
if ($forceAll) {
$personalCircle = true;
}
if ($userId === '') {
$federatedUserService->initCurrentUser();
} else {
$federatedUserService->setLocalCurrentUserId($userId);
}
/** @var CircleService $circleService */
$circleService = \OC::$server->get(CircleService::class);
$probe = new CircleProbe();
$probe->mustBeMember();
$probe->includePersonalCircles($personalCircle);
$probe->filterHiddenCircles();
return $circleService->getCircles($probe);
}
/**
* @param string $circleUniqueId
* @param bool $forceAll
*
* @return Circle
* @throws CircleNotFoundException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InitiatorNotFoundException
* @throws InvalidIdException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
*
* @deprecated - used by apps/dav/lib/Connector/Sabre/Principal.php
* - used by apps/files_sharing/lib/Controller/ShareAPIController.php
* - used by lib/private/Share20/Manager.php
*
* Circles::detailsCircle();
*
* WARNING - This function is called by the core - WARNING
* Do not change it
*
* Returns details on the circle. If the current user is a member, the members list will be
* return as well.
*
*/
public static function detailsCircle(string $circleUniqueId, bool $forceAll = false): Circle {
/** @var FederatedUserService $federatedUserService */
$federatedUserService = \OC::$server->get(FederatedUserService::class);
if ($forceAll) {
$federatedUserService->bypassCurrentUserCondition($forceAll);
} else {
$federatedUserService->initCurrentUser();
}
/** @var CircleService $circleService */
$circleService = \OC::$server->get(CircleService::class);
return $circleService->getCircle($circleUniqueId);
}
/**
* @param string $circleUniqueId
* @param string $ident
* @param int $type
* @param bool $forceAll
*
* @return Membership
*
* @deprecated - used by apps/files_sharing/lib/Controller/ShareAPIController.php
*
* Circles::getMember();
*
* This function will return information on a member of the circle. Current user need at least
* to be Member.
*
*/
public static function getMember($circleUniqueId, $ident, $type, $forceAll = false) {
/** @var CirclesManager $circlesManager */
$circlesManager = \OC::$server->get(CirclesManager::class);
$federatedUser = $circlesManager->getFederatedUser($ident, $type);
return $circlesManager->getLink($circleUniqueId, $federatedUser->getSingleId());
}
/**
* @param array $circleUniqueIds
*
* @return int[] array of object ids or empty array if none found
*
* @deprecated - used by apps/dav/lib/Connector/Sabre/FilesReportPlugin.php
*
* Get a list of objects which are shred with $circleUniqueId.
*
* @since 0.14.0
*
*/
public static function getFilesForCircles(array $circleUniqueIds): array {
try {
$circleService = \OC::$server->get(CircleService::class);
$federatedUserService = \OC::$server->get(FederatedUserService::class);
$shareWrapperService = \OC::$server->get(ShareWrapperService::class);
$federatedUserService->initCurrentUser();
} catch (\Exception $e) {
return [];
}
$result = [];
foreach ($circleUniqueIds as $uniqueId) {
try {
$circleService->getCircle($uniqueId); // checking current user have access to said circle
$files = array_map(
function (ShareWrapper $wrapper): int {
return $wrapper->getFileSource();
}, $shareWrapperService->getSharesToCircle($uniqueId)
);
} catch (\Exception $e) {
$files = [];
}
$result = array_merge($files, $result);
}
return array_values(array_unique($result));
}
}
@@ -0,0 +1,184 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @author Vinicius Cubas Brand <vinicius@eita.org.br>
* @author Daniel Tygel <dtygel@eita.org.br>
*
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\AppInfo;
use Closure;
use OC;
use OCA\Circles\Events\AddingCircleMemberEvent;
use OCA\Circles\Events\CircleMemberAddedEvent;
use OCA\Circles\Events\DestroyingCircleEvent;
use OCA\Circles\Events\Files\CreatingFileShareEvent;
use OCA\Circles\Events\Files\FileShareCreatedEvent;
use OCA\Circles\Events\Files\PreparingFileShareEvent;
use OCA\Circles\Events\PreparingCircleMemberEvent;
use OCA\Circles\Events\RemovingCircleMemberEvent;
use OCA\Circles\Events\RequestingCircleMemberEvent;
use OCA\Circles\Handlers\WebfingerHandler;
use OCA\Circles\Listeners\AccountUpdated;
use OCA\Circles\Listeners\Files\AddingMemberSendMail as ListenerFilesAddingMemberSendMail;
use OCA\Circles\Listeners\Files\CreatingShareSendMail as ListenerFilesCreatingShareSendMail;
use OCA\Circles\Listeners\Files\DestroyingCircle as ListenerFilesDestroyingCircle;
use OCA\Circles\Listeners\Files\ListenerFilesLoadScripts;
use OCA\Circles\Listeners\Files\MemberAddedSendMail as ListenerFilesMemberAddedSendMail;
use OCA\Circles\Listeners\Files\PreparingMemberSendMail as ListenerFilesPreparingMemberSendMail;
use OCA\Circles\Listeners\Files\PreparingShareSendMail as ListenerFilesPreparingShareSendMail;
use OCA\Circles\Listeners\Files\RemovingMember as ListenerFilesRemovingMember;
use OCA\Circles\Listeners\Files\ShareCreatedSendMail as ListenerFilesShareCreatedSendMail;
use OCA\Circles\Listeners\GroupCreated;
use OCA\Circles\Listeners\GroupDeleted;
use OCA\Circles\Listeners\GroupMemberAdded;
use OCA\Circles\Listeners\GroupMemberRemoved;
use OCA\Circles\Listeners\Notifications\RequestingMember as ListenerNotificationsRequestingMember;
use OCA\Circles\Listeners\UserCreated;
use OCA\Circles\Listeners\UserDeleted;
use OCA\Circles\MountManager\CircleMountProvider;
use OCA\Circles\Notification\Notifier;
use OCA\Circles\Search\UnifiedSearchProvider;
use OCA\Circles\Service\ConfigService;
use OCA\Files\App as FilesApp;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCP\Accounts\UserUpdatedEvent;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\Files\Config\IMountProviderCollection;
use OCP\Group\Events\GroupCreatedEvent;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\Group\Events\UserAddedEvent;
use OCP\Group\Events\UserRemovedEvent;
use OCP\IServerContainer;
use OCP\User\Events\UserCreatedEvent;
use OCP\User\Events\UserDeletedEvent;
use Throwable;
/**
* Class Application
*
* @package OCA\Circles\AppInfo
*/
class Application extends App implements IBootstrap {
public const APP_ID = 'circles';
public const APP_NAME = 'Circles';
public const APP_TOKEN = 'dvG7laa0_UU';
public const APP_SUBJECT = 'http://nextcloud.com/';
public const APP_REL = 'https://apps.nextcloud.com/apps/circles';
public const APP_API = 1;
public const CLIENT_TIMEOUT = 3;
private ConfigService $configService;
public function __construct(array $params = []) {
parent::__construct(self::APP_ID, $params);
}
public function register(IRegistrationContext $context): void {
$context->registerCapability(Capabilities::class);
// notification service
$context->registerNotifierService(Notifier::class);
// User Events
$context->registerEventListener(UserCreatedEvent::class, UserCreated::class);
$context->registerEventListener(UserUpdatedEvent::class, AccountUpdated::class);
$context->registerEventListener(UserDeletedEvent::class, UserDeleted::class);
// Group Events
$context->registerEventListener(GroupCreatedEvent::class, GroupCreated::class);
$context->registerEventListener(GroupDeletedEvent::class, GroupDeleted::class);
$context->registerEventListener(UserAddedEvent::class, GroupMemberAdded::class);
$context->registerEventListener(UserRemovedEvent::class, GroupMemberRemoved::class);
// Local Events (for Files/Shares/Notifications management)
$context->registerEventListener(LoadAdditionalScriptsEvent::class, ListenerFilesLoadScripts::class);
$context->registerEventListener(PreparingCircleMemberEvent::class, ListenerFilesPreparingMemberSendMail::class);
$context->registerEventListener(AddingCircleMemberEvent::class, ListenerFilesAddingMemberSendMail::class);
$context->registerEventListener(CircleMemberAddedEvent::class, ListenerFilesMemberAddedSendMail::class);
$context->registerEventListener(PreparingFileShareEvent::class, ListenerFilesPreparingShareSendMail::class);
$context->registerEventListener(CreatingFileShareEvent::class, ListenerFilesCreatingShareSendMail::class);
$context->registerEventListener(FileShareCreatedEvent::class, ListenerFilesShareCreatedSendMail::class);
$context->registerEventListener(RemovingCircleMemberEvent::class, ListenerFilesRemovingMember::class);
$context->registerEventListener(RequestingCircleMemberEvent::class, ListenerNotificationsRequestingMember::class);
$context->registerEventListener(DestroyingCircleEvent::class, ListenerFilesDestroyingCircle::class);
$context->registerSearchProvider(UnifiedSearchProvider::class);
$context->registerWellKnownHandler(WebfingerHandler::class);
}
/**
* @param IBootContext $context
*
* @throws Throwable
*/
public function boot(IBootContext $context): void {
$serverContainer = $context->getServerContainer();
$this->configService = $context->getAppContainer()
->get(ConfigService::class);
$context->injectFn(Closure::fromCallable([$this, 'registerMountProvider']));
$context->injectFn(Closure::fromCallable([$this, 'registerFilesNavigation']));
}
public function registerMountProvider(IServerContainer $container) {
if (!$this->configService->isGSAvailable()) {
return;
}
$mountProviderCollection = $container->get(IMountProviderCollection::class);
$mountProviderCollection->registerProvider($container->get(CircleMountProvider::class));
}
public function registerFilesNavigation() {
$appManager = FilesApp::getNavigationManager();
$appManager->add(
function () {
$l = OC::$server->getL10N('circles');
return [
'id' => 'circlesfilter',
'appname' => 'circles',
'script' => 'files/list.php',
'order' => 25,
'name' => $l->t('Shared to Circles'),
];
}
);
}
}
@@ -0,0 +1,211 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
*
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\AppInfo;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\InterfaceService;
use OCP\App\IAppManager;
use OCP\Capabilities\ICapability;
use OCP\IL10N;
/**
* Class Capabilities
*
* @package OCA\Circles\AppInfo
*/
class Capabilities implements ICapability {
/** @var IL10N */
private $l10n;
/** @var IAppManager */
private $appManager;
/** @var InterfaceService */
private $interfaceService;
/** @var ConfigService */
private $configService;
/**
* Capabilities constructor.
*
* @param IL10N $l10n
* @param IAppManager $appManager
* @param InterfaceService $interfaceService
* @param ConfigService $configService
*/
public function __construct(
IL10N $l10n,
IAppManager $appManager,
InterfaceService $interfaceService,
ConfigService $configService
) {
$this->l10n = $l10n;
$this->appManager = $appManager;
$this->interfaceService = $interfaceService;
$this->configService = $configService;
}
/**
* @param bool $complete
*
* @return array
*/
public function getCapabilities(bool $complete = false): array {
return [
Application::APP_ID => [
'version' => $this->appManager->getAppVersion(Application::APP_ID),
'status' => $this->getCapabilitiesStatus($complete),
'settings' => $this->configService->getSettings(),
'circle' => $this->getCapabilitiesCircle(),
'member' => $this->getCapabilitiesMember()
],
];
}
/**
* @param bool $complete
*
* @return array
*/
private function getCapabilitiesStatus(bool $complete = false): array {
$status = [
'globalScale' => $this->configService->isGSAvailable()
];
if ($complete) {
$status['interfaces'] = [
'all' => $this->interfaceService->getInterfaces(true),
'internal' => $this->interfaceService->getInternalInterfaces(true)
];
}
return $status;
}
/**
* @return array
*/
private function getCapabilitiesCircle(): array {
return [
'constants' => $this->getCapabilitiesCircleConstants(),
'config' => $this->getCapabilitiesCircleConfig()
];
}
/**
* @return array
*/
private function getCapabilitiesMember(): array {
return [
'constants' => $this->getCapabilitiesMemberConstants(),
'type' => Member::$TYPE
];
}
/**
* @return array
*/
private function getCapabilitiesCircleConstants(): array {
return [
'flags' => [
Circle::CFG_SINGLE => $this->l10n->t('Single'),
Circle::CFG_PERSONAL => $this->l10n->t('Personal'),
Circle::CFG_SYSTEM => $this->l10n->t('System'),
Circle::CFG_VISIBLE => $this->l10n->t('Visible'),
Circle::CFG_OPEN => $this->l10n->t('Open'),
Circle::CFG_INVITE => $this->l10n->t('Invite'),
Circle::CFG_REQUEST => $this->l10n->t('Join Request'),
Circle::CFG_FRIEND => $this->l10n->t('Friends'),
Circle::CFG_PROTECTED => $this->l10n->t('Password Protected'),
Circle::CFG_NO_OWNER => $this->l10n->t('No Owner'),
Circle::CFG_HIDDEN => $this->l10n->t('Hidden'),
Circle::CFG_BACKEND => $this->l10n->t('Backend'),
Circle::CFG_LOCAL => $this->l10n->t('Local'),
Circle::CFG_ROOT => $this->l10n->t('Root'),
Circle::CFG_CIRCLE_INVITE => $this->l10n->t('Circle Invite'),
Circle::CFG_FEDERATED => $this->l10n->t('Federated'),
Circle::CFG_MOUNTPOINT => $this->l10n->t('Mount point')
],
'source' =>
[
'core' => [
Member::TYPE_USER => $this->l10n->t('Nextcloud Account'),
Member::TYPE_GROUP => $this->l10n->t('Nextcloud Group'),
Member::TYPE_MAIL => $this->l10n->t('Email Address'),
Member::TYPE_CONTACT => $this->l10n->t('Contact'),
Member::TYPE_CIRCLE => $this->l10n->t('Circle'),
Member::TYPE_APP => $this->l10n->t('Nextcloud App')
],
'extra' => [
Member::APP_CIRCLES => 'Circles App',
Member::APP_OCC => 'Admin Command Line'
]
]
];
}
/**
* @return array
*/
private function getCapabilitiesCircleConfig(): array {
return [
'coreFlags' => Circle::$DEF_CFG_CORE_FILTER,
'systemFlags' => Circle::$DEF_CFG_SYSTEM_FILTER
];
}
/**
* @return array
*/
private function getCapabilitiesMemberConstants(): array {
return [
'level' => [
Member::LEVEL_MEMBER => $this->l10n->t('Member'),
Member::LEVEL_MODERATOR => $this->l10n->t('Moderator'),
Member::LEVEL_ADMIN => $this->l10n->t('Admin'),
Member::LEVEL_OWNER => $this->l10n->t('Owner')
]
];
}
}
@@ -0,0 +1,811 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Circles;
use Exception;
use OC;
use OC\Share20\Share;
use OCA\Circles\AppInfo\Application;
use OCA\Circles\Db\FileSharesRequest;
use OCA\Circles\Db\TokensRequest;
use OCA\Circles\Exceptions\TokenDoesNotExistException;
use OCA\Circles\IBroadcaster;
use OCA\Circles\Model\DeprecatedCircle;
use OCA\Circles\Model\DeprecatedMember;
use OCA\Circles\Model\SharesToken;
use OCA\Circles\Model\SharingFrame;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\MiscService;
use OCA\FederatedFileSharing\Notifications;
use OCP\AppFramework\QueryException;
use OCP\Defaults;
use OCP\Federation\ICloudIdManager;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserManager;
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMailer;
use OCP\Share\Exceptions\IllegalIDChangeException;
use OCP\Share\IShare;
use OCP\Util;
/**
* Class FileSharingBroadcaster
* @deprecated
* @package OCA\Circles\Circles
*/
class FileSharingBroadcaster implements IBroadcaster {
/** @var bool */
private $initiated = false;
/** @var IL10N */
private $l10n = null;
/** @var IMailer */
private $mailer;
/** @var IRootFolder */
private $rootFolder;
/** @var IUserManager */
private $userManager;
/** @var ICloudIdManager */
private $federationCloudIdManager;
/** @var Notifications */
private $federationNotifications;
/** @var ILogger */
private $logger;
/** @var Defaults */
private $defaults;
/** @var IURLGenerator */
private $urlGenerator;
/** @var FileSharesRequest */
private $fileSharesRequest;
/** @var TokensRequest */
private $tokensRequest;
/** @var ConfigService */
private $configService;
/** @var MiscService */
private $miscService;
/** @var bool */
private $federatedEnabled = false;
/**
* {@inheritdoc}
*/
public function init() {
if ($this->initiated) {
return;
}
$this->initiated = true;
$this->l10n = OC::$server->getL10N(Application::APP_ID);
$this->mailer = OC::$server->getMailer();
$this->rootFolder = OC::$server->getLazyRootFolder();
$this->userManager = OC::$server->getUserManager();
$this->federationCloudIdManager = OC::$server->getCloudIdManager();
$this->logger = OC::$server->getLogger();
$this->urlGenerator = OC::$server->getURLGenerator();
try {
$this->defaults = OC::$server->query(Defaults::class);
$this->fileSharesRequest = OC::$server->query(FileSharesRequest::class);
$this->tokensRequest = OC::$server->query(TokensRequest::class);
$this->configService = OC::$server->query(ConfigService::class);
$this->miscService = OC::$server->query(MiscService::class);
} catch (QueryException $e) {
OC::$server->getLogger()
->log(1, 'Circles: cannot init FileSharingBroadcaster - ' . $e->getMessage());
}
try {
$this->federationNotifications =
OC::$server->query(Notifications::class);
$this->federatedEnabled = true;
} catch (QueryException $e) {
}
}
/**
* {@inheritdoc}
*/
public function end() {
}
/**
* {@inheritdoc}
*/
public function createShareToCircle(SharingFrame $frame, DeprecatedCircle $circle) {
if ($frame->is0Circle()) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
public function deleteShareToCircle(SharingFrame $frame, DeprecatedCircle $circle) {
return true;
}
/**
* {@inheritdoc}
*/
public function editShareToCircle(SharingFrame $frame, DeprecatedCircle $circle) {
return true;
}
/**
* {@inheritdoc}
* @throws IllegalIDChangeException
* @throws Exception
*/
public function createShareToMember(SharingFrame $frame, DeprecatedMember $member) {
if (!$frame->is0Circle()) {
return false;
}
$payload = $frame->getPayload();
if (!key_exists('share', $payload)) {
return false;
}
$share = $this->generateShare($payload['share']);
if ($member->getType() === DeprecatedMember::TYPE_MAIL || $member->getType() === DeprecatedMember::TYPE_CONTACT) {
$circle = $frame->getCircle();
try {
// federated shared in contact
$clouds = $this->getCloudsFromContact($member->getUserId());
if ($this->federatedEnabled && !empty($clouds)) {
$sent = false;
foreach ($clouds as $cloudId) {
$sharesToken = $this->tokensRequest->generateTokenForMember($member, $share->getId());
if ($this->sharedByFederated($circle, $share, $cloudId, $sharesToken)) {
$sent = true;
}
}
if ($sent) {
return true;
}
}
$password = '';
$sendPasswordByMail = true;
// if ($this->configService->enforcePasswordProtection($circle)) {
// if ($circle->getSetting('password_single_enabled') === 'true') {
// $password = $circle->getPasswordSingle();
// $sendPasswordByMail = false;
// } else {
// $password = $this->miscService->token(15);
// }
// }
$sharesToken =
$this->tokensRequest->generateTokenForMember($member, $share->getId(), $password);
$mails = [$member->getUserId()];
if ($member->getType() === DeprecatedMember::TYPE_CONTACT) {
$mails = $this->getMailsFromContact($member->getUserId());
}
if (!$sendPasswordByMail) {
$password = '';
}
foreach ($mails as $mail) {
$this->sharedByMail($circle, $share, $mail, $sharesToken, $password);
}
} catch (Exception $e) {
}
}
return true;
}
/**
* {@inheritdoc}
*/
public function deleteShareToMember(SharingFrame $frame, DeprecatedMember $member) {
return true;
}
/**
* {@inheritdoc}
*/
public function editShareToMember(SharingFrame $frame, DeprecatedMember $member) {
return true;
}
/**
* @param DeprecatedCircle $circle
* @param DeprecatedMember $member
*/
public function sendMailAboutExistingShares(DeprecatedCircle $circle, DeprecatedMember $member) {
if ($member->getType() !== DeprecatedMember::TYPE_MAIL && $member->getType() !== DeprecatedMember::TYPE_CONTACT
&& $member->getContactId() !== '') {
return;
}
$this->init();
$allShares = $this->fileSharesRequest->getSharesForCircle($member->getCircleId());
$knownShares = array_map(
function (SharesToken $shareToken) {
return $shareToken->getShareId();
},
$this->tokensRequest->getTokensFromMember($member)
);
$unknownShares = [];
foreach ($allShares as $share) {
if (!in_array($share['id'], $knownShares)) {
$unknownShares[] = $share;
}
}
if ($circle->getViewer() === null) {
$author = $circle->getOwner();
} else {
$author = $circle->getViewer();
}
$recipient = $member->getUserId();
if ($member->getType() === DeprecatedMember::TYPE_CONTACT) {
$data = MiscService::getContactData($member->getUserId());
if (!array_key_exists('EMAIL', $data)) {
return;
}
$emails = $data['EMAIL'];
if (empty($emails)) {
return;
}
$recipient = $emails[0];
}
$this->sendMailExitingShares($circle, $unknownShares, $author, $member, $recipient);
}
/**
* recreate the share from the JSON payload.
*
* @param array $data
*
* @return IShare
* @throws IllegalIDChangeException
*/
private function generateShare($data): IShare {
$this->logger->log(0, 'Regenerate shares from payload: ' . json_encode($data));
$share = new Share($this->rootFolder, $this->userManager);
$share->setId($data['id']);
$share->setSharedBy($data['sharedBy']);
$share->setSharedWith($data['sharedWith']);
$share->setNodeId($data['nodeId']);
$share->setShareOwner($data['shareOwner']);
$share->setPermissions($data['permissions']);
$share->setToken($data['token']);
$share->setPassword($data['password']);
return $share;
}
/**
* @param DeprecatedCircle $circle
* @param IShare $share
* @param string $address
* @param SharesToken $token
*
* @return bool
*/
public function sharedByFederated(DeprecatedCircle $circle, IShare $share, string $address, SharesToken $token
): bool {
try {
$cloudId = $this->federationCloudIdManager->resolveCloudId($address);
$localUrl = $this->urlGenerator->getAbsoluteURL('/');
$sharedByFederatedId = $share->getSharedBy();
if ($this->userManager->userExists($sharedByFederatedId)) {
$cloudId = $this->federationCloudIdManager->getCloudId($sharedByFederatedId, $localUrl);
}
$sharedByFederatedId = $cloudId->getId();
$ownerCloudId = $this->federationCloudIdManager->getCloudId($share->getShareOwner(), $localUrl);
return $this->federationNotifications->sendRemoteShare(
$token->getToken(),
$address,
$share->getNode()
->getName(),
$share->getId(),
$share->getShareOwner(),
$ownerCloudId->getId(),
$share->getSharedBy(),
$sharedByFederatedId,
IShare::TYPE_USER
);
} catch (\Exception $e) {
$this->logger->logException(
$e, [
'message' => 'Failed to notify remote server of circles-federated share',
'level' => ILogger::ERROR,
'app' => 'circles',
]
);
}
return false;
}
/**
* @param DeprecatedCircle $circle
* @param IShare $share
* @param string $email
* @param SharesToken $sharesToken
* @param string $password
*/
private function sharedByMail(
DeprecatedCircle $circle, IShare $share, string $email, SharesToken $sharesToken, string $password
) {
// genelink
$link = $this->urlGenerator->linkToRouteAbsolute(
'files_sharing.sharecontroller.showShare',
['token' => $sharesToken->getToken()]
);
$lang = $this->configService->getCoreValueForUser($share->getSharedBy(), 'lang', '');
if ($lang !== '') {
$this->l10n = OC::$server->getL10N(Application::APP_ID, $lang);
}
$displayName = $this->miscService->getDisplayName($share->getSharedBy());
try {
$this->sendMail(
$share->getNode()
->getName(), $link, $displayName, $circle->getName(), $email
);
$this->sendPasswordByMail($share, $displayName, $email, $password);
} catch (Exception $e) {
OC::$server->getLogger()
->log(1, 'Circles::sharedByMail - mail were not sent: ' . $e->getMessage());
}
}
/**
* @param $fileName
* @param string $link
* @param string $author
* @param $circleName
* @param string $email
*
* @throws Exception
*/
protected function sendMail($fileName, $link, $author, $circleName, $email) {
$message = $this->mailer->createMessage();
$this->logger->log(
0, "Sending mail to circle '" . $circleName . "': " . $email . ' file: ' . $fileName
. ' - link: ' . $link
);
$subject = $this->l10n->t('%s shared »%s« with you.', [$author, $fileName]);
$text = $this->l10n->t('%s shared »%s« with "%s".', [$author, $fileName, $circleName]);
$emailTemplate =
$this->generateEmailTemplate($subject, $text, $fileName, $link, $author, $circleName);
$instanceName = $this->defaults->getName();
$senderName = $this->l10n->t('%s on %s', [$author, $instanceName]);
$message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]);
$emailTemplate->setSubject($subject);
$message->useTemplate($emailTemplate);
$message->setTo([$email]);
$this->mailer->send($message);
}
/**
* @param IShare $share
* @param string $circleName
* @param string $email
*
* @param $password
*
* @throws NotFoundException
* @throws Exception
*/
protected function sendPasswordByMail(IShare $share, $circleName, $email, $password) {
// if (!$this->configService->sendPasswordByMail() || $password === '') {
// return;
// }
$message = $this->mailer->createMessage();
$this->logger->log(0, "Sending password mail to circle '" . $circleName . "': " . $email);
$filename = $share->getNode()
->getName();
$initiator = $share->getSharedBy();
$shareWith = $share->getSharedWith();
$initiatorUser = $this->userManager->get($initiator);
$initiatorDisplayName =
($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
$initiatorEmailAddress =
($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
$plainBodyPart = $this->l10n->t(
"%1\$s shared »%2\$s« with you.\nYou should have already received a separate email with a link to access it.\n",
[$initiatorDisplayName, $filename]
);
$htmlBodyPart = $this->l10n->t(
'%1$s shared »%2$s« with you. You should have already received a separate email with a link to access it.',
[$initiatorDisplayName, $filename]
);
$emailTemplate = $this->mailer->createEMailTemplate(
'sharebymail.RecipientPasswordNotification', [
'filename' => $filename,
'password' => $password,
'initiator' => $initiatorDisplayName,
'initiatorEmail' => $initiatorEmailAddress,
'shareWith' => $shareWith,
]
);
$emailTemplate->setSubject(
$this->l10n->t(
'Password to access »%1$s« shared to you by %2$s', [$filename, $initiatorDisplayName]
)
);
$emailTemplate->addHeader();
$emailTemplate->addHeading($this->l10n->t('Password to access »%s«', [$filename]), false);
$emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart);
$emailTemplate->addBodyText($this->l10n->t('It is protected with the following password:'));
$emailTemplate->addBodyText($password);
// The "From" contains the sharers name
$instanceName = $this->defaults->getName();
$senderName = $this->l10n->t(
'%1$s via %2$s',
[
$initiatorDisplayName,
$instanceName
]
);
$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
if ($initiatorEmailAddress !== null) {
$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
} else {
$emailTemplate->addFooter();
}
$message->setTo([$email]);
$message->useTemplate($emailTemplate);
$this->mailer->send($message);
}
/**
* @param $subject
* @param $text
* @param $fileName
* @param $link
* @param string $author
* @param string $circleName
*
* @return IEMailTemplate
*/
private function generateEmailTemplate($subject, $text, $fileName, $link, $author, $circleName
) {
$emailTemplate = $this->mailer->createEMailTemplate(
'circles.ShareNotification', [
'fileName' => $fileName,
'fileLink' => $link,
'author' => $author,
'circleName' => $circleName,
]
);
$emailTemplate->addHeader();
$emailTemplate->addHeading($subject, false);
$emailTemplate->addBodyText(
htmlspecialchars($text) . '<br>' . htmlspecialchars(
$this->l10n->t('Click the button below to open it.')
), $text
);
$emailTemplate->addBodyButton(
$this->l10n->t('Open »%s«', [htmlspecialchars($fileName)]), $link
);
return $emailTemplate;
}
/**
* @param DeprecatedCircle $circle
* @param array $unknownShares
* @param DeprecatedMember $author
* @param DeprecatedMember $member
* @param string $recipient
*/
public function sendMailExitingShares(
DeprecatedCircle $circle, array $unknownShares, DeprecatedMember $author, DeprecatedMember $member, string $recipient
) {
$data = [];
$password = '';
// if ($this->configService->enforcePasswordProtection($circle)) {
// $password = $this->miscService->token(15);
// }
foreach ($unknownShares as $share) {
try {
$data[] = $this->getMailLinkFromShare($share, $member, $password);
} catch (TokenDoesNotExistException $e) {
}
}
if (sizeof($data) === 0) {
return;
}
try {
$template = $this->generateMailExitingShares($author->getCachedName(), $circle->getName());
$this->fillMailExistingShares($template, $data);
$this->sendMailExistingShares($template, $author->getCachedName(), $recipient);
$this->sendPasswordExistingShares($author, $recipient, $password);
} catch (Exception $e) {
$this->logger->log(2, 'Failed to send mail about existing share ' . $e->getMessage());
}
}
/**
* @param $author
* @param string $email
* @param $password
*
* @throws Exception
*/
protected function sendPasswordExistingShares(DeprecatedMember $author, string $email, string $password) {
// if (!$this->configService->sendPasswordByMail() || $password === '') {
// return;
// }
$message = $this->mailer->createMessage();
$authorUser = $this->userManager->get($author->getUserId());
$authorName = ($authorUser instanceof IUser) ? $authorUser->getDisplayName() : $author;
$authorEmail = ($authorUser instanceof IUser) ? $authorUser->getEMailAddress() : null;
$this->logger->log(0, "Sending password mail about existing files to '" . $email . "'");
$plainBodyPart = $this->l10n->t(
"%1\$s shared multiple files with you.\nYou should have already received a separate email with a link to access them.\n",
[$authorName]
);
$htmlBodyPart = $this->l10n->t(
'%1$s shared multiple files with you. You should have already received a separate email with a link to access them.',
[$authorName]
);
$emailTemplate = $this->mailer->createEMailTemplate(
'sharebymail.RecipientPasswordNotification', [
'password' => $password,
'author' => $author
]
);
$emailTemplate->setSubject(
$this->l10n->t(
'Password to access files shared to you by %1$s', [$authorName]
)
);
$emailTemplate->addHeader();
$emailTemplate->addHeading($this->l10n->t('Password to access files'), false);
$emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart);
$emailTemplate->addBodyText($this->l10n->t('It is protected with the following password:'));
$emailTemplate->addBodyText($password);
// The "From" contains the sharers name
$instanceName = $this->defaults->getName();
$senderName = $this->l10n->t(
'%1$s via %2$s',
[
$authorName,
$instanceName
]
);
$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
if ($authorEmail !== null) {
$message->setReplyTo([$authorEmail => $authorName]);
$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
} else {
$emailTemplate->addFooter();
}
$message->setTo([$email]);
$message->useTemplate($emailTemplate);
$this->mailer->send($message);
}
/**
* @param array $share
* @param DeprecatedMember $member
* @param string $password
*
* @return array
* @throws TokenDoesNotExistException
*/
private function getMailLinkFromShare(array $share, DeprecatedMember $member, string $password = '') {
$sharesToken = $this->tokensRequest->generateTokenForMember($member, $share['id'], $password);
$link = $this->urlGenerator->linkToRouteAbsolute(
'files_sharing.sharecontroller.showShare',
['token' => $sharesToken->getToken()]
);
$author = $share['uid_initiator'];
$filename = basename($share['file_target']);
return [
'author' => $author,
'link' => $link,
'filename' => $filename
];
}
/**
* @param $author
* @param string $circleName
*
* @return IEMailTemplate
* @throws Exception
*/
protected function generateMailExitingShares($author, $circleName) {
$this->logger->log(
0, "Generating mail about existing share mail from '" . $author . "' in "
. $circleName
);
$emailTemplate = $this->mailer->createEMailTemplate('circles.ExistingShareNotification', []);
$emailTemplate->addHeader();
$text = $this->l10n->t('%s shared multiple files with "%s".', [$author, $circleName]);
$emailTemplate->addBodyText(htmlspecialchars($text), $text);
return $emailTemplate;
}
/**
* @param IEMailTemplate $emailTemplate
* @param array $data
*/
protected function fillMailExistingShares(IEMailTemplate $emailTemplate, array $data) {
foreach ($data as $item) {
// $text = $this->l10n->t('%s shared »%s« with you.', [$item['author'], $item['filename']]);
// $emailTemplate->addBodyText(
// htmlspecialchars($text) . '<br>' . htmlspecialchars(
// $this->l10n->t('Click the button below to open it.')
// ), $text
// );
$emailTemplate->addBodyButton(
$this->l10n->t('Open »%s«', [htmlspecialchars($item['filename'])]), $item['link']
);
}
}
/**
* @param IEMailTemplate $emailTemplate
* @param $author
* @param $recipient
*
* @throws Exception
*/
protected function sendMailExistingShares(IEMailTemplate $emailTemplate, $author, $recipient) {
$subject = $this->l10n->t('%s shared multiple files with you.', [$author]);
// $emailTemplate->addHeading($subject, false);
$instanceName = $this->defaults->getName();
$senderName = $this->l10n->t('%s on %s', [$author, $instanceName]);
$message = $this->mailer->createMessage();
$message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]);
$message->setSubject($subject);
$message->setPlainBody($emailTemplate->renderText());
$message->setHtmlBody($emailTemplate->renderHtml());
$message->setTo([$recipient]);
$this->mailer->send($message);
}
/**
* @param string $contactId
*
* @return array
*/
private function getCloudsFromContact(string $contactId): array {
$contact = MiscService::getContactData($contactId);
if (!key_exists('CLOUD', $contact)) {
return [];
}
return $contact['CLOUD'];
}
/**
* @param string $contactId
*
* @return array
*/
private function getMailsFromContact(string $contactId): array {
$contact = MiscService::getContactData($contactId);
if (!key_exists('EMAIL', $contact)) {
return [];
}
return $contact['EMAIL'];
}
}
@@ -0,0 +1,576 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\ContactAddressBookNotFoundException;
use OCA\Circles\Exceptions\ContactFormatException;
use OCA\Circles\Exceptions\ContactNotFoundException;
use OCA\Circles\Exceptions\FederatedEventException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotConfirmedException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\MembershipNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\FederatedUser;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\Membership;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Model\Probes\DataProbe;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\MemberService;
use OCA\Circles\Service\MembershipService;
use OCA\Circles\Tools\Exceptions\InvalidItemException;
/**
* Class CirclesManager
*
* @package OCA\Circles
*/
class CirclesManager {
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/** @var MemberService */
private $memberService;
/** @var MembershipService */
private $membershipService;
/** @var ConfigService */
private $configService;
/** @var CirclesQueryHelper */
private $circlesQueryHelper;
/**
* CirclesManager constructor.
*
* @param FederatedUserService $federatedUserService
* @param CircleService $circleService
* @param MemberService $memberService
* @param MembershipService $membershipService
* @param ConfigService $configService
* @param CirclesQueryHelper $circlesQueryHelper
*/
public function __construct(
FederatedUserService $federatedUserService,
CircleService $circleService,
MemberService $memberService,
MembershipService $membershipService,
ConfigService $configService,
CirclesQueryHelper $circlesQueryHelper
) {
$this->federatedUserService = $federatedUserService;
$this->circleService = $circleService;
$this->memberService = $memberService;
$this->membershipService = $membershipService;
$this->configService = $configService;
$this->circlesQueryHelper = $circlesQueryHelper;
}
/**
* @param string $federatedId
* @param int $type
*
* @return FederatedUser
* @throws CircleNotFoundException
* @throws FederatedItemException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InvalidIdException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
*/
public function getFederatedUser(string $federatedId, int $type = Member::TYPE_SINGLE): FederatedUser {
return $this->federatedUserService->getFederatedUser($federatedId, $type);
}
/**
* @param string $userId
*
* @return FederatedUser
* @throws CircleNotFoundException
* @throws FederatedItemException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InvalidIdException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
*/
public function getLocalFederatedUser(string $userId): FederatedUser {
return $this->getFederatedUser($userId, Member::TYPE_USER);
}
/**
* @throws FederatedUserNotFoundException
* @throws SingleCircleNotFoundException
* @throws RequestBuilderException
* @throws InvalidIdException
* @throws FederatedUserException
*/
public function startSession(?FederatedUser $federatedUser = null): void {
if (is_null($federatedUser)) {
$this->federatedUserService->initCurrentUser();
} else {
$this->federatedUserService->setCurrentUser($federatedUser);
}
}
/**
*
*/
public function startSuperSession(): void {
$this->federatedUserService->unsetCurrentUser();
$this->federatedUserService->bypassCurrentUserCondition(true);
}
/**
* @param string $appId
* @param int $appSerial
*
* @throws ContactAddressBookNotFoundException
* @throws ContactFormatException
* @throws ContactNotFoundException
* @throws FederatedUserException
* @throws InvalidIdException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
*/
public function startAppSession(string $appId, int $appSerial = Member::APP_DEFAULT): void {
$this->federatedUserService->setLocalCurrentApp($appId, $appSerial);
}
/**
* $userId - userId to emulate as initiator (can be empty)
* $userType - specify if userIs not a singleId
* $circleId - if no userId specified, will use the owner of the Circle as initiator
*
* @param string $userId
* @param int $userType
* @param string $circleId
*
* @throws CircleNotFoundException
* @throws FederatedItemException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InvalidIdException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
*/
public function startOccSession(
string $userId,
int $userType = Member::TYPE_SINGLE,
string $circleId = ''
): void {
$this->federatedUserService->commandLineInitiator($userId, $userType, $circleId);
}
/**
*
*/
public function stopSession(): void {
$this->federatedUserService->unsetCurrentUser();
$this->federatedUserService->bypassCurrentUserCondition(false);
}
/**
* @return IFederatedUser
* @throws FederatedUserNotFoundException
*/
public function getCurrentFederatedUser(): IFederatedUser {
$current = $this->federatedUserService->getCurrentUser();
if (is_null($current)) {
throw new FederatedUserNotFoundException();
}
return $current;
}
/**
* @return CirclesQueryHelper
*/
public function getQueryHelper(): CirclesQueryHelper {
return $this->circlesQueryHelper;
}
/**
* @param string $name
* @param FederatedUser|null $owner
* @param bool $personal
* @param bool $local
*
* @return Circle
* @throws FederatedEventException
* @throws InitiatorNotConfirmedException
* @throws FederatedItemException
* @throws InitiatorNotFoundException
* @throws InvalidItemException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws UnknownRemoteException
*/
public function createCircle(
string $name,
?FederatedUser $owner = null,
bool $personal = false,
bool $local = false
): Circle {
$outcome = $this->circleService->create($name, $owner, $personal, $local);
$circle = new Circle();
$circle->import($outcome);
return $circle;
}
/**
* @param string $singleId
*
* @throws CircleNotFoundException
* @throws FederatedEventException
* @throws FederatedItemException
* @throws InitiatorNotConfirmedException
* @throws InitiatorNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws UnknownRemoteException
*/
public function destroyCircle(string $singleId): void {
$this->circleService->destroy($singleId);
}
/**
* WARNING: This method is not using Cached Memberships meaning that the request can be heavy and should
* only be used if probeCircles() does not fit your need.
*
* Always prefer probeCircles();
*
* returns available Circles to the current session.
*
* @see probeCircles()
*
* @return Circle[]
* @throws InitiatorNotFoundException
* @throws RequestBuilderException
*/
public function getCircles(?CircleProbe $probe = null, bool $refreshCache = false): array {
if (is_null($probe)) {
$probe = new CircleProbe();
$probe->filterHiddenCircles()
->filterBackendCircles();
}
return $this->circleService->getCircles($probe, !$refreshCache);
}
/**
* @param string $singleId
* @param CircleProbe|null $probe
*
* @return Circle
* @throws CircleNotFoundException
* @throws InitiatorNotFoundException
* @throws RequestBuilderException
*/
public function getCircle(string $singleId, ?CircleProbe $probe = null): Circle {
return $this->circleService->getCircle($singleId, $probe);
}
/**
* @param Circle $circle
*
* @throws CircleNotFoundException
* @throws FederatedEventException
* @throws FederatedItemException
* @throws InitiatorNotConfirmedException
* @throws InitiatorNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws UnknownRemoteException
*/
public function updateConfig(Circle $circle): void {
$this->circleService->updateConfig($circle->getSingleId(), $circle->getConfig());
}
/**
* @param string $circleId
* @param bool $enabled
*
* @throws CircleNotFoundException
* @throws FederatedEventException
* @throws FederatedItemException
* @throws FederatedUserException
* @throws InitiatorNotConfirmedException
* @throws InitiatorNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws UnknownRemoteException
*/
public function flagAsAppManaged(string $circleId, bool $enabled = true): void {
$this->federatedUserService->confirmSuperSession();
$this->federatedUserService->setOwnerAsCurrentUser($circleId);
$probe = new CircleProbe();
$probe->includeSystemCircles();
$localCircle = $this->circleService->getCircle($circleId, $probe);
if (!$this->configService->isLocalInstance($localCircle->getInstance())) {
throw new CircleNotFoundException('This Circle is not managed from this instance');
}
$config = $localCircle->getConfig();
if ($enabled) {
$config |= Circle::CFG_APP;
} else {
$config &= ~Circle::CFG_APP;
}
$this->circleService->updateConfig($circleId, $config);
}
/**
* @param string $circleId
* @param FederatedUser $federatedUser
*
* @return Member
* @throws CircleNotFoundException
* @throws ContactAddressBookNotFoundException
* @throws ContactFormatException
* @throws ContactNotFoundException
* @throws FederatedEventException
* @throws FederatedItemException
* @throws FederatedUserException
* @throws InitiatorNotConfirmedException
* @throws InitiatorNotFoundException
* @throws InvalidIdException
* @throws InvalidItemException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
* @throws UnknownRemoteException
*/
public function addMember(string $circleId, FederatedUser $federatedUser): Member {
$outcome = $this->memberService->addMember($circleId, $federatedUser);
$member = new Member();
$member->import($outcome);
return $member;
}
/**
* @param string $memberId
* @param int $level
*
* @return Member
* @throws FederatedEventException
* @throws FederatedItemException
* @throws InitiatorNotConfirmedException
* @throws InitiatorNotFoundException
* @throws InvalidItemException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws UnknownRemoteException
*/
public function levelMember(string $memberId, int $level): Member {
$outcome = $this->memberService->memberLevel($memberId, $level);
$member = new Member();
$member->import($outcome);
return $member;
}
/**
* @param string $memberId
*
* @throws FederatedEventException
* @throws FederatedItemException
* @throws InitiatorNotConfirmedException
* @throws InitiatorNotFoundException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws UnknownRemoteException
*/
public function removeMember(string $memberId): void {
$this->memberService->removeMember($memberId);
}
/**
* @param string $circleId
* @param string $singleId
* @param bool $detailed
*
* @return Membership
* @throws MembershipNotFoundException
* @throws RequestBuilderException
*/
public function getLink(string $circleId, string $singleId, bool $detailed = false): Membership {
return $this->membershipService->getMembership($circleId, $singleId, $detailed);
}
/**
* @param IEntity $circle
*
* @return string
*/
public function getDefinition(IEntity $circle): string {
return $this->circleService->getDefinition($circle);
}
/**
* Returns data about Circles based on cached Memberships.
* Meaning that only Circles the current user is a member will be returned.
*
* CircleProbe is used to filter Circles to be returned by the method.
* DataProbe is used to add details to returned Circles.
*
* @param CircleProbe|null $circleProbe
* @param DataProbe|null $dataProbe
*
* @return array
* @throws InitiatorNotFoundException
* @throws RequestBuilderException
*/
public function probeCircles(?CircleProbe $circleProbe = null, ?DataProbe $dataProbe = null): array {
if (is_null($circleProbe)) {
$circleProbe = new CircleProbe();
$circleProbe->filterHiddenCircles()
->filterBackendCircles();
}
return $this->circleService->probeCircles($circleProbe, $dataProbe);
}
/**
* WIP
*
* @param string $circleId
* @param string $singleId
*
* @return Member
* @throws InitiatorNotFoundException
* @throws MemberNotFoundException
* @throws RequestBuilderException
*/
// public function getMember(string $circleId, string $singleId): Member {
// $this->federatedUserService->bypassCurrentUserCondition(true);
// $this->memberService->getMemberById($circleId, $singleId);
// }
/**
* WIP
*
* @param string $memberId
*
* @return Member
*/
// public function getMemberById(string $memberId): Member {
}
@@ -0,0 +1,195 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles;
use OCA\Circles\Db\CoreQueryBuilder;
use OCA\Circles\Db\CoreRequestBuilder;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\FederatedUserService;
use OCP\DB\QueryBuilder\ICompositeExpression;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* Class CirclesQueryHelper
*
* @package OCA\Circles
*/
class CirclesQueryHelper {
/** @var CoreRequestBuilder */
private $coreRequestBuilder;
/** @var CoreQueryBuilder */
private $queryBuilder;
/** @var FederatedUserService */
private $federatedUserService;
/**
* CirclesQueryHelper constructor.
*
* @param CoreRequestBuilder $coreRequestBuilder
* @param FederatedUserService $federatedUserService
*/
public function __construct(
CoreRequestBuilder $coreRequestBuilder,
FederatedUserService $federatedUserService
) {
$this->coreRequestBuilder = $coreRequestBuilder;
$this->federatedUserService = $federatedUserService;
}
/**
* @return IQueryBuilder
*/
public function getQueryBuilder(): IQueryBuilder {
$this->queryBuilder = $this->coreRequestBuilder->getQueryBuilder();
return $this->queryBuilder;
}
/**
* @param string $alias
* @param string $field
* @param bool $fullDetails
*
* @return ICompositeExpression
* @throws RequestBuilderException
* @throws FederatedUserNotFoundException
*/
public function limitToSession(
string $alias,
string $field,
bool $fullDetails = false
): ICompositeExpression {
$session = $this->federatedUserService->getCurrentUser();
if (is_null($session)) {
throw new FederatedUserNotFoundException('session not initiated');
}
$this->queryBuilder->setDefaultSelectAlias($alias);
$this->queryBuilder->setOptions(
[CoreQueryBuilder::HELPER],
[
'getData' => $fullDetails,
'minimumLevel' => Member::LEVEL_MEMBER
]
);
return $this->queryBuilder->limitToInitiator(
CoreQueryBuilder::HELPER,
$session,
$field,
$alias
);
}
/**
* @param string $alias
* @param string $field
* @param IFederatedUser $federatedUser
* @param bool $fullDetails
*
* @return ICompositeExpression
* @throws RequestBuilderException
*/
public function limitToInheritedMembers(
string $alias,
string $field,
IFederatedUser $federatedUser,
bool $fullDetails = false
): ICompositeExpression {
$this->queryBuilder->setDefaultSelectAlias($alias);
$this->queryBuilder->setOptions(
[CoreQueryBuilder::HELPER],
[
'getData' => $fullDetails,
'filterPersonalCircles' => true,
'includePersonalCircles' => true,
'minimumLevel' => Member::LEVEL_MEMBER
]
);
return $this->queryBuilder->limitToInitiator(
CoreQueryBuilder::HELPER,
$federatedUser,
$field,
$alias
);
}
/**
* @param string $field
* @param string $alias
*
* @throws RequestBuilderException
*/
public function addCircleDetails(
string $alias,
string $field
): void {
$this->queryBuilder->setDefaultSelectAlias($alias);
$this->queryBuilder->setOptions(
[CoreQueryBuilder::HELPER],
[
'getData' => true
]
);
$this->queryBuilder->leftJoinCircle(CoreQueryBuilder::HELPER, null, $field, $alias);
}
/**
* @param array $data
*
* @return Circle
* @throws CircleNotFoundException
*/
public function extractCircle(array $data): Circle {
$circle = new Circle();
$circle->importFromDatabase(
$data,
CoreQueryBuilder::HELPER . '_' . CoreQueryBuilder::CIRCLE . '_'
);
return $circle;
}
}
@@ -0,0 +1,169 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Collaboration\v2;
use Exception;
use OCA\Circles\AppInfo\Application;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Model\Probes\DataProbe;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Tools\Traits\TNCLogger;
use OCP\Collaboration\Collaborators\ISearchPlugin;
use OCP\Collaboration\Collaborators\ISearchResult;
use OCP\Collaboration\Collaborators\SearchResultType;
use OCP\IRequest;
use OCP\Share\IShare;
/**
* Class CollaboratorSearchPlugin
*
* @package OCA\Circles\Collaboration\v2
*/
class CollaboratorSearchPlugin implements ISearchPlugin {
use TNCLogger;
/** @var IRequest */
private $request;
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/**
* CollaboratorSearchPlugin constructor.
*
* @param IRequest $request
* @param FederatedUserService $federatedUserService
* @param CircleService $circleService
*/
public function __construct(
IRequest $request,
FederatedUserService $federatedUserService,
CircleService $circleService
) {
$this->request = $request;
$this->federatedUserService = $federatedUserService;
$this->circleService = $circleService;
$this->setup('app', Application::APP_ID);
}
/**
* @param string $search
* @param int $limit
* @param int $offset
* @param ISearchResult $searchResult
*
* @return bool
*/
public function search($search, $limit, $offset, ISearchResult $searchResult): bool {
$wide = $exact = [];
$fromFrontEnd = true;
// TODO: remove this, using a cleaner way to detect the source of the request
$params = $this->request->getParams();
$shareType = $this->getArray('shareType', $params);
if (empty($shareType) || in_array(IShare::TYPE_ROOM, $shareType)) {
$fromFrontEnd = false;
}
$filterCircle = new Circle();
$filterCircle->setName($search)
->setDisplayName($search);
try {
$this->federatedUserService->initCurrentUser();
$probe = new CircleProbe();
$probe->filterBackendCircles()
->filterSystemCircles()
->setItemsLimit($limit)
->setItemsOffset($offset)
->setFilterCircle($filterCircle);
// If from the OCS API, we use getCircles(), to get more complex result at the price of huge resource,
// if not (ie. share popup) we only need probeCircles()
if ($fromFrontEnd) {
$probe->mustBeMember(false)
->filterConfig(Circle::CFG_ROOT, true);
$circles = $this->circleService->getCircles($probe);
} else {
$dataProbe = new DataProbe();
$dataProbe->add(DataProbe::OWNER);
$circles = $this->circleService->probeCircles($probe, $dataProbe);
}
} catch (Exception $e) {
return false;
}
foreach ($circles as $circle) {
$entry = $this->addResultEntry($circle);
if (strtolower($circle->getName()) === strtolower($search)) {
$exact[] = $entry;
} else {
$wide[] = $entry;
}
}
$type = new SearchResultType('circles');
$searchResult->addResultSet($type, $wide, $exact);
return false;
}
/**
* @param Circle $circle
*
* @return array
*/
private function addResultEntry(Circle $circle): array {
return [
'label' => $circle->getDisplayName(),
'shareWithDescription' => $this->circleService->getDefinition($circle),
'value' => [
'shareType' => IShare::TYPE_CIRCLE,
'shareWith' => $circle->getSingleId(),
'circle' => $circle
],
];
}
}
@@ -0,0 +1,896 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use Exception;
use OC;
use OC\AppConfig;
use OC\Core\Command\Base;
use OCA\Circles\AppInfo\Application;
use OCA\Circles\AppInfo\Capabilities;
use OCA\Circles\Exceptions\FederatedEventException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\InitiatorNotConfirmedException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\UnknownInterfaceException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\FederatedItems\LoopbackTest;
use OCA\Circles\Model\Federated\FederatedEvent;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedEventService;
use OCA\Circles\Service\InterfaceService;
use OCA\Circles\Service\RemoteService;
use OCA\Circles\Service\RemoteStreamService;
use OCA\Circles\Service\RemoteUpstreamService;
use OCA\Circles\Tools\Exceptions\RequestNetworkException;
use OCA\Circles\Tools\Exceptions\SignatoryException;
use OCA\Circles\Tools\Model\NCRequest;
use OCA\Circles\Tools\Model\Request;
use OCA\Circles\Tools\Model\SimpleDataStore;
use OCA\Circles\Tools\Traits\TArrayTools;
use OCA\Circles\Tools\Traits\TNCRequest;
use OCA\Circles\Tools\Traits\TStringTools;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Class CirclesCheck
*
* @package OCA\Circles\Command
*/
class CirclesCheck extends Base {
use TStringTools;
use TArrayTools;
use TNCRequest;
public static $checks = [
'internal',
'frontal',
'loopback'
];
/** @var Capabilities */
private $capabilities;
/** @var InterfaceService */
private $interfaceService;
/** @var FederatedEventService */
private $federatedEventService;
/** @var RemoteService */
private $remoteService;
/** @var RemoteStreamService */
private $remoteStreamService;
/** @var RemoteUpstreamService */
private $remoteUpstreamService;
/** @var ConfigService */
private $configService;
/** @var array */
private $sessions = [];
/**
* CirclesCheck constructor.
*
* @param Capabilities $capabilities
* @param InterfaceService $interfaceService
* @param FederatedEventService $federatedEventService
* @param RemoteService $remoteService
* @param RemoteStreamService $remoteStreamService
* @param RemoteUpstreamService $remoteUpstreamService
* @param ConfigService $configService
*/
public function __construct(
Capabilities $capabilities,
InterfaceService $interfaceService,
FederatedEventService $federatedEventService,
RemoteService $remoteService,
RemoteStreamService $remoteStreamService,
RemoteUpstreamService $remoteUpstreamService,
ConfigService $configService
) {
parent::__construct();
$this->capabilities = $capabilities;
$this->interfaceService = $interfaceService;
$this->federatedEventService = $federatedEventService;
$this->remoteService = $remoteService;
$this->remoteStreamService = $remoteStreamService;
$this->remoteUpstreamService = $remoteUpstreamService;
$this->configService = $configService;
}
protected function configure() {
parent::configure();
$this->setName('circles:check')
->setDescription('Checking your configuration')
->addOption('capabilities', '', InputOption::VALUE_NONE, 'listing app\'s capabilities')
->addOption('type', '', InputOption::VALUE_REQUIRED, 'configuration to check', '')
->addOption('alpha', '', InputOption::VALUE_NONE, 'allow ALPHA features')
->addOption('test', '', InputOption::VALUE_REQUIRED, 'specify an url to test', '');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws Exception
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
if ($input->getOption('capabilities')) {
$capabilities = $this->getArray('circles', $this->capabilities->getCapabilities(true));
$output->writeln(json_encode($capabilities, JSON_PRETTY_PRINT));
return 0;
}
$this->configService->setAppValue(ConfigService::TEST_NC_BASE, '');
$test = $input->getOption('test');
$type = $input->getOption('type');
if ($test !== '' && $type === '') {
throw new Exception('Please specify a --type for the test');
}
if ($test !== '' && !in_array($type, self::$checks)) {
throw new Exception('Unknown type: ' . implode(', ', self::$checks));
}
// $this->configService->setAppValue(ConfigService::TEST_NC_BASE, $test);
if ($type === '' || $type === 'loopback') {
$output->writeln('### Checking <info>loopback</info> address.');
$this->checkLoopback($input, $output, $test);
$output->writeln('');
$output->writeln('');
}
if ($type === '' || $type === 'internal') {
$output->writeln('### Testing <info>internal</info> address.');
$this->checkInternal($input, $output, $test);
$output->writeln('');
$output->writeln('');
}
if (!$input->getOption('alpha')) {
return 0;
}
if ($type === '' || $type === 'frontal') {
$output->writeln('### Testing <info>frontal</info> address.');
$this->checkFrontal($input, $output, $test);
$output->writeln('');
}
return 0;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @param string $test
*
* @throws Exception
*/
private function checkLoopback(InputInterface $input, OutputInterface $output, string $test = ''): void {
$output->writeln('. The <info>loopback</info> setting is mandatory and can be checked locally.');
$output->writeln(
'. The address you need to define here must be a reachable url of your Nextcloud from the hosting server itself.'
);
$output->writeln(
'. By default, the App will use the entry \'overwrite.cli.url\' from \'config/config.php\'.'
);
$notDefault = false;
if ($test === '') {
$test = $this->configService->getLoopbackPath();
} else {
$notDefault = true;
}
$output->writeln('');
$output->writeln('* testing current address: ' . $test);
try {
$this->setupLoopback($input, $output, $test);
$output->writeln('* <info>Loopback</info> address looks good');
if ($notDefault) {
$this->saveLoopback($input, $output, $test);
}
return;
} catch (Exception $e) {
}
$output->writeln('');
$output->writeln('- <comment>You do not have a valid loopback address setup right now.</comment>');
$output->writeln('');
$helper = $this->getHelper('question');
while (true) {
$question = new Question(
'<info>Please write down a new loopback address to test</info>: ', ''
);
$loopback = $helper->ask($input, $output, $question);
if (is_null($loopback) || $loopback === '') {
$output->writeln('exiting.');
throw new Exception('Your Circles App is not fully configured.');
}
try {
[$scheme, $cloudId, $path] = $this->parseAddress($loopback);
} catch (Exception $e) {
$output->writeln('<error>format must be http[s]://domain.name[:post][/path]</error>');
continue;
}
$loopback = rtrim($scheme . '://' . $cloudId . $path, '/');
$output->writeln('* testing address: ' . $loopback . ' ');
try {
$this->setupLoopback($input, $output, $loopback);
$this->saveLoopback($input, $output, $loopback);
return;
} catch (Exception $e) {
$output->writeln('');
}
}
}
/**
* @throws Exception
*/
private function setupLoopback(InputInterface $input, OutputInterface $output, string $address): void {
$e = null;
try {
[$scheme, $cloudId, $path] = $this->parseAddress($address);
$this->configService->setAppValue(ConfigService::LOOPBACK_TMP_SCHEME, $scheme);
$this->configService->setAppValue(ConfigService::LOOPBACK_TMP_ID, $cloudId);
$this->configService->setAppValue(ConfigService::LOOPBACK_TMP_PATH, $path);
if (!$this->testLoopback($input, $output)) {
throw new Exception();
}
} catch (Exception $e) {
}
$this->configService->setAppValue(ConfigService::LOOPBACK_TMP_SCHEME, '');
$this->configService->setAppValue(ConfigService::LOOPBACK_TMP_ID, '');
$this->configService->setAppValue(ConfigService::LOOPBACK_TMP_PATH, '');
if (!is_null($e)) {
throw $e;
}
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return bool
* @throws FederatedEventException
* @throws FederatedItemException
* @throws InitiatorNotConfirmedException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws UnknownRemoteException
*/
private function testLoopback(InputInterface $input, OutputInterface $output): bool {
if (!$this->testRequest($output, 'GET', 'core.CSRFToken.index')) {
return false;
}
if (!$this->testRequest(
$output, 'POST', 'circles.EventWrapper.asyncBroadcast',
['token' => 'test-dummy-token']
)) {
return false;
}
$timer = round(microtime(true) * 1000);
$output->write('- Creating async FederatedEvent ');
$test = new FederatedEvent(LoopbackTest::class);
$this->federatedEventService->newEvent($test);
$output->writeln(
'<info>' . $test->getWrapperToken() . '</info> ' .
'(took ' . (round(microtime(true) * 1000) - $timer) . 'ms)'
);
$output->writeln('- Waiting for async process to finish (5s)');
sleep(5);
$output->write('- Checking status on FederatedEvent ');
$wrappers = $this->remoteUpstreamService->getEventsByToken($test->getWrapperToken());
if (count($wrappers) !== 1) {
$output->writeln('<error>Event created too many Wrappers</error>');
return false;
}
$wrapper = array_shift($wrappers);
$checkVerify = $wrapper->getEvent()->getData()->gInt('verify');
if ($checkVerify === LoopbackTest::VERIFY) {
$output->write('<info>verify=' . $checkVerify . '</info> ');
} else {
$output->writeln('<error>verify=' . $checkVerify . '</error>');
return false;
}
$checkManage = $wrapper->getResult()->gInt('manage');
if ($checkManage === LoopbackTest::MANAGE) {
$output->write('<info>manage=' . $checkManage . '</info> ');
} else {
$output->writeln('<error>manage=' . $checkManage . '</error>');
return false;
}
$output->writeln('');
return true;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @param string $loopback
*
* @throws Exception
*/
private function saveLoopback(InputInterface $input, OutputInterface $output, string $loopback): void {
[$scheme, $cloudId, $path] = $this->parseAddress($loopback);
$question = new ConfirmationQuestion(
'- Do you want to save <info>'
. $loopback
. '</info> as your <info>loopback</info> address ? (y/N) ',
false, '/^(y|Y)/i'
);
$helper = $this->getHelper('question');
if (!$helper->ask($input, $output, $question)) {
$output->writeln('skipping.');
return;
}
$this->configService->setAppValue(ConfigService::LOOPBACK_CLOUD_SCHEME, $scheme);
$this->configService->setAppValue(ConfigService::LOOPBACK_CLOUD_ID, $cloudId);
$this->configService->setAppValue(ConfigService::LOOPBACK_CLOUD_PATH, $path);
$output->writeln(
'- Address <info>' . $loopback . '</info> is now used as <info>loopback</info>'
);
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @param string $test
*
* @throws SignatoryException
* @throws UnknownInterfaceException
* @throws Exception
*/
private function checkInternal(InputInterface $input, OutputInterface $output, string $test): void {
$output->writeln(
'. The <info>internal</info> setting should only be enabled if you are willing to use Circles in a GlobalScale setup on a local network.'
);
$output->writeln(
'. The address you need to define here is the local address of your Nextcloud, reachable by all other instances of our GlobalScale.'
);
$question = new ConfirmationQuestion(
'- <comment>Do you want to enable this feature ?</comment> (y/N) ', false, '/^(y|Y)/i'
);
$helper = $this->getHelper('question');
if (!$helper->ask($input, $output, $question)) {
$output->writeln('skipping.');
return;
}
while (true) {
$output->writeln('');
$question = new Question(
'<info>Please write down a new internal address to test</info>: ', ''
);
$internal = $helper->ask($input, $output, $question);
if (is_null($internal) || $internal === '') {
$output->writeln('skipping.');
return;
}
try {
[$scheme, $cloudId, $path] = $this->parseAddress($internal);
} catch (Exception $e) {
$output->writeln('<error>format must be http[s]://domain.name[:post][/path]</error>');
continue;
}
$internal = rtrim($scheme . '://' . $cloudId, '/');
$fullInternal = rtrim($scheme . '://' . $cloudId . $path, '/');
$question = new ConfirmationQuestion(
'<comment>Do you want to check the validity of this internal address?</comment> (Y/n) ', true,
'/^(y|Y)/i'
);
if ($helper->ask($input, $output, $question)) {
$testToken = $this->token();
$this->configService->setAppValue(ConfigService::IFACE_TEST_ID, $cloudId);
$this->configService->setAppValue(ConfigService::IFACE_TEST_SCHEME, $scheme);
$this->configService->setAppValue(ConfigService::IFACE_TEST_PATH, $path);
$this->configService->setAppValue(ConfigService::IFACE_TEST_TOKEN, $testToken);
$output->writeln('');
$output->writeln(
'You will need to run this <info>curl</info> command from a terminal on your local network and paste its result: '
);
$output->writeln(
' curl -L "' . $internal
. '/.well-known/webfinger?resource=http://nextcloud.com/&test='
. $testToken . '"'
);
$output->writeln('paste the result here: ');
$question = new Question('', '');
$pastedWebfinger = new SimpleDataStore();
$pastedWebfinger->json(trim($helper->ask($input, $output, $question)));
if ($pastedWebfinger->g('subject') !== Application::APP_SUBJECT) {
$output->writeln('<error>Cannot extract SUBJECT from the pasted data</error>');
continue;
}
$pastedHref = '';
foreach ($pastedWebfinger->gArray('links') as $link) {
$entry = new SimpleDataStore($link);
if ($entry->g('rel') === Application::APP_REL) {
$pastedHref = $entry->g('href');
}
}
if ($pastedHref === '') {
$output->writeln('<error>Cannot retrieve HREF from the pasted data</error>');
continue;
}
$href = $this->interfaceService->getCloudPath(
'circles.Remote.appService',
[],
InterfaceService::IFACE_TEST
);
if ($pastedHref !== $href) {
$output->writeln(
'<error>The returned data (' . $pastedHref . ') are not the one expected: </error>'
. $href
);
continue;
}
$output->writeln('');
$output->writeln('<info>First step seems fine.</info>');
$output->writeln(
'Next step, please run this <info>curl</info> command from a terminal on your local network and paste its result: '
);
$output->writeln(
' curl -L "' . $pastedHref . '?test=' . $testToken . '" -H "Accept: application/json"'
);
$output->writeln('paste the result here: ');
$question = new Question('', '');
$pastedSignatory = new SimpleDataStore();
$pastedSignatory->json(trim($helper->ask($input, $output, $question)));
// small hack to refresh the cached config
OC::$server->get(AppConfig::class)->clearCachedConfig();
$this->interfaceService->setCurrentInterface(InterfaceService::IFACE_TEST);
$appSignatory = $this->remoteStreamService->getAppSignatory(false);
if ($appSignatory->getUid(true) !== $pastedSignatory->g('uid')
|| $appSignatory->getRoot() !== $pastedSignatory->g('root')) {
$output->writeln(
'<error>The returned data ('
. $pastedSignatory->g('uid') . '/' . $pastedSignatory->g('root')
. ') are not the one expected: </error>'
. $appSignatory->getUid(true) . '/' . $appSignatory->getRoot()
);
continue;
}
$output->writeln('* <info>Internal</info> address looks good');
}
$this->saveInternal($input, $output, $fullInternal);
return;
}
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @param string $internal
*
* @throws Exception
*/
private function saveInternal(InputInterface $input, OutputInterface $output, string $internal): void {
[$scheme, $cloudId, $path] = $this->parseAddress($internal);
$output->writeln('');
$question = new ConfirmationQuestion(
'- Do you want to save <info>' . $internal
. '</info> as your <info>internal</info> address ? (y/N) ',
false, '/^(y|Y)/i'
);
$helper = $this->getHelper('question');
if (!$helper->ask($input, $output, $question)) {
$output->writeln('skipping.');
return;
}
$this->configService->setAppValue(ConfigService::INTERNAL_CLOUD_SCHEME, $scheme);
$this->configService->setAppValue(ConfigService::INTERNAL_CLOUD_ID, $cloudId);
$this->configService->setAppValue(ConfigService::INTERNAL_CLOUD_PATH, $path);
$output->writeln('- Address <info>' . $internal . '</info> is now used as <info>internal</info>');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @param string $test
*/
private function checkFrontal(InputInterface $input, OutputInterface $output, string $test): void {
$output->writeln('. The <info>frontal</info> setting is optional.');
$output->writeln(
'. The purpose of this address is for your Federated Circle to reach other instances of Nextcloud over the Internet.'
);
$output->writeln(
'. The address you need to define here must be reachable from the Internet.'
);
$output->writeln(
'. By default, this feature is disabled.'
);
$question = new ConfirmationQuestion(
'- <comment>Do you want to enable this feature ?</comment> (y/N) ', false, '/^(y|Y)/i'
);
$helper = $this->getHelper('question');
if (!$helper->ask($input, $output, $question)) {
$output->writeln('skipping.');
return;
}
while (true) {
$question = new Question(
'<info>Please write down a new frontal address to test</info>: ', ''
);
$frontal = $helper->ask($input, $output, $question);
if (is_null($frontal) || $frontal === '') {
$output->writeln('skipping.');
return;
}
try {
[$scheme, $cloudId, $path] = $this->parseAddress($frontal);
} catch (Exception $e) {
$output->writeln('<error>format must be http[s]://domain.name[:post][/path]</error>');
continue;
}
$frontal = rtrim($scheme . '://' . $cloudId, '/');
$fullFrontal = rtrim($scheme . '://' . $cloudId . $path, '/');
$question = new ConfirmationQuestion(
'<comment>Do you want to check the validity of this frontal address?</comment> (y/N) ', false,
'/^(y|Y)/i'
);
if ($helper->ask($input, $output, $question)) {
$testToken = $this->token();
$this->configService->setAppValue(ConfigService::IFACE_TEST_ID, $cloudId);
$this->configService->setAppValue(ConfigService::IFACE_TEST_SCHEME, $scheme);
$this->configService->setAppValue(ConfigService::IFACE_TEST_PATH, $path);
$this->configService->setAppValue(ConfigService::IFACE_TEST_TOKEN, $testToken);
$output->writeln('');
$output->writeln(
'You will need to run this <info>curl</info> command from a remote terminal and paste its result: '
);
$output->writeln(
' curl -L "' . $frontal
. '/.well-known/webfinger?resource=http://nextcloud.com/&test='
. $testToken . '"'
);
$output->writeln('paste the result here: ');
$question = new Question('', '');
$pastedWebfinger = new SimpleDataStore();
$pastedWebfinger->json(trim($helper->ask($input, $output, $question)));
if ($pastedWebfinger->g('subject') !== Application::APP_SUBJECT) {
$output->writeln('<error>Cannot extract SUBJECT from the pasted data</error>');
continue;
}
$pastedHref = '';
foreach ($pastedWebfinger->gArray('links') as $link) {
$entry = new SimpleDataStore($link);
if ($entry->g('rel') === Application::APP_REL) {
$pastedHref = $entry->g('href');
}
}
if ($pastedHref === '') {
$output->writeln('<error>Cannot retrieve HREF from the pasted data</error>');
continue;
}
$href = $this->interfaceService->getCloudPath(
'circles.Remote.appService',
[],
InterfaceService::IFACE_TEST
);
if ($pastedHref !== $href) {
$output->writeln(
'<error>The returned data (' . $pastedHref . ') are not the one expected: </error>'
. $href
);
continue;
}
$output->writeln('');
$output->writeln('<info>First step seems fine.</info>');
$output->writeln(
'Next step, please run this <info>curl</info> command from a remote terminal and paste its result: '
);
$output->writeln(
' curl -L "' . $pastedHref . '?test=' . $testToken . '" -H "Accept: application/json"'
);
$output->writeln('paste the result here: ');
$question = new Question('', '');
$pastedSignatory = new SimpleDataStore();
$pastedSignatory->json(trim($helper->ask($input, $output, $question)));
// small hack to refresh the cached config
OC::$server->get(AppConfig::class)->clearCachedConfig();
$this->interfaceService->setCurrentInterface(InterfaceService::IFACE_TEST);
$appSignatory = $this->remoteStreamService->getAppSignatory(false);
if ($appSignatory->getUid(true) !== $pastedSignatory->g('uid')
|| $appSignatory->getRoot() !== $pastedSignatory->g('root')) {
$output->writeln(
'<error>The returned data ('
. $pastedSignatory->g('uid') . '/' . $pastedSignatory->g('root')
. ') are not the one expected: </error>'
. $appSignatory->getUid(true) . '/' . $appSignatory->getRoot()
);
continue;
}
$output->writeln('* <info>Frontal</info> address looks good');
}
$this->saveFrontal($input, $output, $fullFrontal);
return;
}
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @param string $frontal
*
* @throws Exception
*/
private function saveFrontal(InputInterface $input, OutputInterface $output, string $frontal): void {
[$scheme, $cloudId, $path] = $this->parseAddress($frontal);
$output->writeln('');
$question = new ConfirmationQuestion(
'- Do you want to save <info>' . $frontal
. '</info> as your <info>frontal</info> address ? (y/N) ',
false, '/^(y|Y)/i'
);
$helper = $this->getHelper('question');
if (!$helper->ask($input, $output, $question)) {
$output->writeln('skipping.');
return;
}
$this->configService->setAppValue(ConfigService::FRONTAL_CLOUD_SCHEME, $scheme);
$this->configService->setAppValue(ConfigService::FRONTAL_CLOUD_ID, $cloudId);
$this->configService->setAppValue(ConfigService::FRONTAL_CLOUD_PATH, $path);
$output->writeln('- Address <info>' . $frontal . '</info> is now used as <info>frontal</info>');
}
/**
* @param OutputInterface $o
* @param string $type
* @param string $route
* @param array $args
*
* @return bool
*/
private function testRequest(
OutputInterface $output,
string $type,
string $route,
array $args = []
): bool {
$request = new NCRequest('', Request::type($type));
$this->configService->configureLoopbackRequest($request, $route, $args);
$request->setFollowLocation(false);
if ($request->getType() !== Request::TYPE_GET) {
$request->setDataSerialize(new SimpleDataStore(['empty' => 1]));
}
$output->write('- ' . $type . ' request on ' . $request->getCompleteUrl() . ': ');
try {
$this->doRequest($request);
$result = $request->getResult();
$color = 'error';
if ($result->getStatusCode() === 200) {
$color = 'info';
}
$output->writeln('<' . $color . '>' . $result->getStatusCode() . '</' . $color . '>');
if ($result->getStatusCode() === 200) {
return true;
}
} catch (RequestNetworkException $e) {
$output->writeln('<error>fail</error>');
}
return false;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @param string $address
*/
private function saveUrl(InputInterface $input, OutputInterface $output, string $address): void {
if ($address === '') {
return;
}
$output->writeln('');
$output->writeln(
'The address <info>' . $address . '</info> seems to reach your local Nextcloud.'
);
$helper = $this->getHelper('question');
$output->writeln('');
$question = new ConfirmationQuestion(
'<info>Do you want to store this address in database ?</info> (y/N) ', false, '/^(y|Y)/i'
);
if (!$helper->ask($input, $output, $question)) {
$output->writeln('Configuration NOT saved');
return;
}
$this->configService->setAppValue(ConfigService::FORCE_NC_BASE, $address);
$output->writeln(
'New configuration <info>' . Application::APP_ID . '.' . ConfigService::FORCE_NC_BASE . '=\''
. $address . '\'</info> stored in database'
);
}
/**
* @param string $test
*
* @return array
* @throws Exception
*/
private function parseAddress(string $test): array {
$scheme = parse_url($test, PHP_URL_SCHEME);
$cloudId = parse_url($test, PHP_URL_HOST);
$cloudIdPort = parse_url($test, PHP_URL_PORT);
$path = parse_url($test, PHP_URL_PATH);
if (is_bool($scheme) || is_bool($cloudId) || is_null($scheme) || is_null($cloudId)) {
throw new Exception();
}
if (is_null($path) || is_bool($path)) {
$path = '';
}
$path = rtrim($path, '/');
if (!is_null($cloudIdPort)) {
$cloudId = $cloudId . ':' . $cloudIdPort;
}
return [$scheme, $cloudId, $path];
}
}
@@ -0,0 +1,249 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedEventException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotConfirmedException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\FederatedUserService;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CirclesConfig
*
* @package OCA\Circles\Command
*/
class CirclesConfig extends Base {
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/**
* CirclesConfig constructor.
*
* @param FederatedUserService $federatedUserService
* @param CircleService $circlesService
*/
public function __construct(FederatedUserService $federatedUserService, CircleService $circlesService) {
parent::__construct();
$this->federatedUserService = $federatedUserService;
$this->circleService = $circlesService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:manage:config')
->setDescription('edit config/type of a Circle')
->addArgument('circle_id', InputArgument::REQUIRED, 'ID of the circle')
->addArgument(
'config', InputArgument::IS_ARRAY,
'list of value to change in the configuration of the Circle'
)
->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
->addOption('initiator-type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0')
->addOption(
'super-session', '',
InputOption::VALUE_NONE, 'use super session to bypass some condition'
)
->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws FederatedEventException
* @throws FederatedItemException
* @throws InitiatorNotFoundException
* @throws RequestBuilderException
* @throws CircleNotFoundException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InitiatorNotConfirmedException
* @throws InvalidIdException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws SingleCircleNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$circleId = (string)$input->getArgument('circle_id');
try {
$this->federatedUserService->commandLineInitiator(
$input->getOption('initiator'),
Member::parseTypeString($input->getOption('initiator-type')),
$circleId,
false
);
if ($input->getOption('super-session')) {
$this->federatedUserService->bypassCurrentUserCondition(true);
}
$circle = $this->circleService->getCircle($circleId);
if (empty($input->getArgument('config'))) {
$output->writeln(
json_encode(Circle::getCircleFlags($circle, Circle::FLAGS_LONG), JSON_PRETTY_PRINT)
);
return 0;
}
$new = $this->generateConfig($circle, $input->getArgument('config'));
$outcome = $this->circleService->updateConfig($circleId, $new);
} catch (FederatedItemException $e) {
if ($input->getOption('status-code')) {
throw new FederatedItemException(
' [' . get_class($e) . ', ' . $e->getStatus() . ']' . "\n" . $e->getMessage()
);
}
throw $e;
}
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($outcome, JSON_PRETTY_PRINT));
} elseif (strtolower($input->getOption('output')) !== 'none') {
$circle = $this->circleService->getCircle($circleId);
$output->writeln(
json_encode(
Circle::getCircleFlags($circle, Circle::FLAGS_LONG),
JSON_PRETTY_PRINT
)
);
}
return 0;
}
/**
* @param Circle $circle
* @param array $listing
*
* @return int
*/
private function generateConfig(Circle $circle, array $listing): int {
$current = clone $circle;
$valid = $this->filterValidConfig($current);
foreach ($listing as $item) {
$add = true;
if (substr($item, 0, 1) === '_') {
$add = false;
$item = substr($item, 1);
}
$value = array_search(strtoupper($item), $valid);
if (!$value) {
throw new InvalidArgumentException(
'Invalid config \'' . $item . '\'. Available values: '
. implode(', ', array_values($valid)) . '. '
. 'To disable a config, start the value with an underscore'
);
}
if ($add) {
$current->addConfig($value);
} else {
$current->remConfig($value);
}
}
return $current->getConfig();
}
/**
* @param Circle $circle
*
* @return array
*/
private function filterValidConfig(Circle $circle): array {
$listing = Circle::$DEF_CFG;
$filters = Circle::$DEF_CFG_CORE_FILTER;
if (!$circle->isConfig(Circle::CFG_SYSTEM)) {
$filters = array_merge($filters, Circle::$DEF_CFG_SYSTEM_FILTER);
}
foreach ($filters as $filter) {
unset($listing[$filter]);
}
array_walk(
$listing,
function (string &$v): void {
[, $long] = explode('|', $v);
$v = strtoupper(str_replace(' ', '', $long));
}
);
return $listing;
}
}
@@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Tools\Exceptions\InvalidItemException;
use OCA\Circles\Tools\Traits\TDeserialize;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CirclesCreate
*
* @package OCA\Circles\Command
*/
class CirclesCreate extends Base {
use TDeserialize;
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/**
* CirclesCreate constructor.
*
* @param FederatedUserService $federatedUserService
* @param CircleService $circleService
*/
public function __construct(
FederatedUserService $federatedUserService,
CircleService $circleService
) {
parent::__construct();
$this->federatedUserService = $federatedUserService;
$this->circleService = $circleService;
}
protected function configure() {
parent::configure();
$this->setName('circles:manage:create')
->setDescription('create a new circle')
->addArgument('owner', InputArgument::REQUIRED, 'owner of the circle')
->addArgument('name', InputArgument::REQUIRED, 'name of the circle')
->addOption('personal', '', InputOption::VALUE_NONE, 'create a personal circle')
->addOption('local', '', InputOption::VALUE_NONE, 'create a local circle')
->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception')
->addOption(
'type', '', InputOption::VALUE_REQUIRED, 'type of the owner',
Member::$TYPE[Member::TYPE_SINGLE]
);
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws FederatedItemException
* @throws InitiatorNotFoundException
* @throws SingleCircleNotFoundException
* @throws RequestBuilderException
* @throws InvalidItemException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$ownerId = $input->getArgument('owner');
$name = $input->getArgument('name');
try {
$this->federatedUserService->bypassCurrentUserCondition(true);
$type = Member::parseTypeString($input->getOption('type'));
$owner = $this->federatedUserService->getFederatedUser($ownerId, $type);
$outcome = $this->circleService->create(
$name,
$owner,
$input->getOption('personal'),
$input->getOption('local')
);
} catch (FederatedItemException $e) {
if ($input->getOption('status-code')) {
throw new FederatedItemException(
' [' . get_class($e) . ', ' . $e->getStatus() . ']' . "\n" . $e->getMessage()
);
}
throw $e;
}
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($outcome, JSON_PRETTY_PRINT));
} elseif (strtolower($input->getOption('output')) !== 'none') {
/** @var Circle $circle */
$circle = $this->deserialize($outcome, Circle::class);
$output->writeln('Id: <info>' . $circle->getSingleId() . '</info>');
$output->writeln('Name: <info>' . $circle->getDisplayName() . '</info>');
$output->writeln('Owner: <info>' . $circle->getOwner()->getDisplayName() . '</info>');
}
return 0;
}
}
@@ -0,0 +1,144 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\FederatedUserService;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CirclesDestroy
*
* @package OCA\Circles\Command
*/
class CirclesDestroy extends Base {
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/**
* CirclesDestroy constructor.
*
* @param CircleService $circleService
*/
public function __construct(FederatedUserService $federatedUserService, CircleService $circleService) {
parent::__construct();
$this->federatedUserService = $federatedUserService;
$this->circleService = $circleService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:manage:destroy')
->setDescription('destroy a circle by its ID')
->addArgument('circle_id', InputArgument::REQUIRED, 'ID of the circle to be destroyed')
->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
->addOption('initiator-type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0')
->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws CircleNotFoundException
* @throws FederatedItemException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InvalidIdException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws SingleCircleNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
* @throws RequestBuilderException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$circleId = $input->getArgument('circle_id');
try {
$this->federatedUserService->commandLineInitiator(
$input->getOption('initiator'),
Member::parseTypeString($input->getOption('initiator-type')),
$circleId,
false
);
$outcome = $this->circleService->destroy($circleId);
} catch (FederatedItemException $e) {
if ($input->getOption('status-code')) {
throw new FederatedItemException(
' [' . get_class($e) . ', ' . $e->getStatus() . ']' . "\n" . $e->getMessage()
);
}
throw $e;
}
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($outcome, JSON_PRETTY_PRINT));
}
return 0;
}
}
@@ -0,0 +1,192 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OCA\Circles\Tools\Exceptions\InvalidItemException;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\MemberService;
use OCA\Circles\Service\RemoteService;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CirclesDetails
*
* @package OCA\Circles\Command
*/
class CirclesDetails extends Base {
/** @var FederatedUserService */
private $federatedUserService;
/** @var RemoteService */
private $remoteService;
/** @var MemberService */
private $memberService;
/** @var CircleService */
private $circleService;
/** @var ConfigService */
private $configService;
/**
* CirclesDetails constructor.
*
* @param FederatedUserService $federatedUserService
* @param RemoteService $remoteService
* @param CircleService $circlesService
* @param MemberService $membersService
* @param ConfigService $configService
*/
public function __construct(
FederatedUserService $federatedUserService, RemoteService $remoteService,
CircleService $circlesService, MemberService $membersService, ConfigService $configService
) {
parent::__construct();
$this->federatedUserService = $federatedUserService;
$this->remoteService = $remoteService;
$this->circleService = $circlesService;
$this->memberService = $membersService;
$this->configService = $configService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:manage:details')
->setDescription('get details about a circle by its ID')
->addArgument('circle_id', InputArgument::REQUIRED, 'ID of the circle')
->addOption('instance', '', InputOption::VALUE_REQUIRED, 'Instance of the circle', '')
->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
->addOption('initiator-type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0')
->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws CircleNotFoundException
* @throws FederatedItemException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InitiatorNotFoundException
* @throws InvalidItemException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
* @throws InvalidIdException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$circleId = (string)$input->getArgument('circle_id');
$instance = $input->getOption('instance');
try {
if ($instance !== '') {
$circle = $this->remoteService->getCircleFromInstance(
$circleId,
$instance,
[
'initiator' => $input->getOption('initiator'),
'initiatorType' => Member::parseTypeString($input->getOption('initiator-type'))
]
);
} else {
try {
$this->federatedUserService->commandLineInitiator(
$input->getOption('initiator'),
Member::parseTypeString($input->getOption('initiator-type')),
$circleId,
true
);
$probe = new CircleProbe();
$probe->includeNonVisibleCircles();
$circle = $this->circleService->getCircle($circleId, $probe);
} catch (CircleNotFoundException $e) {
throw new CircleNotFoundException(
'unknown circle, use --instance to retrieve the data from a remote instance'
);
}
}
} catch (FederatedItemException $e) {
if ($input->getOption('status-code')) {
throw new FederatedItemException(
' [' . get_class($e) . ', ' . $e->getStatus() . ']' . "\n" . $e->getMessage()
);
}
throw $e;
}
$output->writeln(json_encode($circle, JSON_PRETTY_PRINT));
return 0;
}
}
@@ -0,0 +1,169 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCP\IL10N;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CirclesEdit
*
* @package OCA\Circles\Command
*/
class CirclesEdit extends Base {
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/**
* CirclesEdit constructor.
*
* @param IL10N $l10n
* @param FederatedUserService $federatedUserService
* @param CircleService $circlesService
* @param ConfigService $configService
*/
public function __construct(
IL10N $l10n, FederatedUserService $federatedUserService, CircleService $circlesService,
ConfigService $configService
) {
parent::__construct();
$this->federatedUserService = $federatedUserService;
$this->circleService = $circlesService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:manage:edit')
->setDescription('edit displayName or description of a Circle')
->addArgument('circle_id', InputArgument::REQUIRED, 'ID of the circle')
->addArgument('edit', InputArgument::REQUIRED, 'displayName or description')
->addArgument('value', InputArgument::REQUIRED, 'new value')
->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
->addOption('initiator-type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0')
->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws FederatedItemException
* @throws CircleNotFoundException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InvalidIdException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws SingleCircleNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
* @throws RequestBuilderException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$circleId = (string)$input->getArgument('circle_id');
$edit = strtolower((string)$input->getArgument('edit'));
$newValue = (string)$input->getArgument('value');
try {
$this->federatedUserService->commandLineInitiator(
$input->getOption('initiator'),
Member::parseTypeString($input->getOption('initiator-type')),
$circleId,
false
);
switch ($edit) {
case 'displayname':
$outcome = $this->circleService->updateName($circleId, $newValue);
break;
case 'description':
$outcome = $this->circleService->updateDescription($circleId, $newValue);
break;
default:
throw new InvalidArgumentException('edit can only be \'displayName\' or \'description\'');
}
} catch (FederatedItemException $e) {
if ($input->getOption('status-code')) {
throw new FederatedItemException(
' [' . get_class($e) . ', ' . $e->getStatus() . ']' . "\n" . $e->getMessage()
);
}
throw $e;
}
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($outcome, JSON_PRETTY_PRINT));
}
return 0;
}
}
@@ -0,0 +1,155 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedEventException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotConfirmedException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\FederatedUserService;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CirclesJoin
*
* @package OCA\Circles\Command
*/
class CirclesJoin extends Base {
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/**
* CirclesJoin constructor.
*
* @param FederatedUserService $federatedUserService
* @param CircleService $circlesService
*/
public function __construct(
FederatedUserService $federatedUserService,
CircleService $circlesService
) {
parent::__construct();
$this->federatedUserService = $federatedUserService;
$this->circleService = $circlesService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:manage:join')
->setDescription('emulate a user joining a Circle')
->addArgument('circle_id', InputArgument::REQUIRED, 'ID of the circle')
->addArgument('initiator', InputArgument::REQUIRED, 'initiator to the request')
->addOption('type', '', InputOption::VALUE_REQUIRED, 'type of the initiator', '0')
->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws FederatedEventException
* @throws FederatedItemException
* @throws InitiatorNotFoundException
* @throws CircleNotFoundException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InitiatorNotConfirmedException
* @throws InvalidIdException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$circleId = (string)$input->getArgument('circle_id');
try {
$this->federatedUserService->commandLineInitiator(
$input->getArgument('initiator'),
Member::parseTypeString($input->getOption('type')),
'',
false
);
$outcome = $this->circleService->circleJoin($circleId);
} catch (FederatedItemException $e) {
if ($input->getOption('status-code')) {
throw new FederatedItemException(
' [' . get_class($e) . ', ' . $e->getStatus() . ']' . "\n" . $e->getMessage()
);
}
throw $e;
}
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($outcome, JSON_PRETTY_PRINT));
}
return 0;
}
}
@@ -0,0 +1,135 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\FederatedEventException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CirclesLeave
*
* @package OCA\Circles\Command
*/
class CirclesLeave extends Base {
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/** @var ConfigService */
private $configService;
/**
* CirclesLeave constructor.
*
* @param FederatedUserService $federatedUserService
* @param CircleService $circlesService
* @param ConfigService $configService
*/
public function __construct(
FederatedUserService $federatedUserService, CircleService $circlesService,
ConfigService $configService
) {
parent::__construct();
$this->federatedUserService = $federatedUserService;
$this->circleService = $circlesService;
$this->configService = $configService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:manage:leave')
->setDescription('simulate a user joining a Circle')
->addArgument('circle_id', InputArgument::REQUIRED, 'ID of the circle')
->addArgument('initiator', InputArgument::REQUIRED, 'initiator to the request')
->addOption('type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0')
->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws FederatedEventException
* @throws FederatedItemException
* @throws InitiatorNotFoundException
* @throws RequestBuilderException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$circleId = (string)$input->getArgument('circle_id');
try {
$this->federatedUserService->commandLineInitiator(
$input->getArgument('initiator'),
Member::parseTypeString($input->getOption('type')),
'',
false
);
$outcome = $this->circleService->circleLeave($circleId);
} catch (FederatedItemException $e) {
if ($input->getOption('status-code')) {
throw new FederatedItemException(
' [' . get_class($e) . ', ' . $e->getStatus() . ']' . "\n" . $e->getMessage()
);
}
throw $e;
}
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($outcome, JSON_PRETTY_PRINT));
}
return 0;
}
}
@@ -0,0 +1,267 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OCA\Circles\Tools\Exceptions\RequestNetworkException;
use OCA\Circles\Tools\Exceptions\SignatoryException;
use OCA\Circles\Tools\Traits\TArrayTools;
use OCA\Circles\Tools\Traits\TStringTools;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\ModelManager;
use OCA\Circles\Model\Probes\BasicProbe;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\RemoteService;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CirclesList
*
* @package OCA\Circles\Command
*/
class CirclesList extends Base {
use TArrayTools;
use TStringTools;
/** @var ModelManager */
private $modelManager;
/** @var FederatedUserService */
private $federatedUserService;
/** @var RemoteService */
private $remoteService;
/** @var CircleService */
private $circleService;
/** @var ConfigService */
private $configService;
/** @var InputInterface */
private $input;
/**
* CirclesList constructor.
*
* @param ModelManager $modelManager
* @param FederatedUserService $federatedUserService
* @param RemoteService $remoteService
* @param CircleService $circleService
* @param ConfigService $configService
*/
public function __construct(
ModelManager $modelManager, FederatedUserService $federatedUserService, RemoteService $remoteService,
CircleService $circleService, ConfigService $configService
) {
parent::__construct();
$this->modelManager = $modelManager;
$this->federatedUserService = $federatedUserService;
$this->remoteService = $remoteService;
$this->circleService = $circleService;
$this->configService = $configService;
}
protected function configure() {
parent::configure();
$this->setName('circles:manage:list')
->setDescription('listing current circles')
->addOption('instance', '', InputOption::VALUE_REQUIRED, 'Instance of the circle', '')
->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
->addOption('initiator-type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0')
->addOption('member', '', InputOption::VALUE_REQUIRED, 'search for member', '')
->addOption('def', '', InputOption::VALUE_NONE, 'display complete circle configuration')
->addOption('display-name', '', InputOption::VALUE_NONE, 'display the displayName')
->addOption('personal', '', InputOption::VALUE_NONE, 'include Personal Circles')
->addOption('system', '', InputOption::VALUE_NONE, 'include System Circles')
->addOption('hidden', '', InputOption::VALUE_NONE, 'include Hidden Circles')
->addOption('backend', '', InputOption::VALUE_NONE, 'include Backend Circles')
->addOption('single', '', InputOption::VALUE_NONE, 'returns only Single Circles')
->addOption('all', '', InputOption::VALUE_NONE, 'include all Circles');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws CircleNotFoundException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InitiatorNotFoundException
* @throws InvalidIdException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestNetworkException
* @throws SignatoryException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
* @throws FederatedItemException
* @throws MemberNotFoundException
* @throws SingleCircleNotFoundException
* @throws RequestBuilderException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$this->input = $input;
$member = $input->getOption('member');
$instance = $input->getOption('instance');
$initiator = $input->getOption('initiator');
$filterMember = null;
if ($member !== '') {
$filterMember = $this->federatedUserService->getFederatedMember($member);
}
if (!$this->configService->isLocalInstance($instance)) {
$data = ['filterMember' => $filterMember];
if ($initiator) {
$data['initiator'] = $this->federatedUserService->getFederatedUser(
$initiator,
Member::parseTypeString($input->getOption('initiator-type')),
);
}
$circles = $this->remoteService->getCirclesFromInstance($instance, $data);
} else {
$this->federatedUserService->commandLineInitiator(
$initiator,
Member::parseTypeString($input->getOption('initiator-type')),
'',
true
);
$probe = new CircleProbe();
$probe->filterHiddenCircles()
->filterBackendCircles()
->addDetail(BasicProbe::DETAILS_POPULATION);
if ($input->getOption('system')) {
$probe->includeSystemCircles();
$probe->filterHiddenCircles(false);
}
$probe->includeHiddenCircles($input->getOption('hidden'));
$probe->includeBackendCircles($input->getOption('backend'));
$probe->includePersonalCircles($input->getOption('personal'));
if ($input->getOption('single')) {
$singleCircle = new Circle();
$singleCircle->setConfig(Circle::CFG_SINGLE);
$probe->setFilterCircle($singleCircle)
->includeSingleCircles();
}
if ($input->getOption('all')) {
$probe->includeSystemCircles();
$probe->includeHiddenCircles();
$probe->includeBackendCircles();
$probe->includePersonalCircles();
$probe->includeSingleCircles();
}
$circles = $this->circleService->getCircles($probe);
}
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($circles, JSON_PRETTY_PRINT));
return 0;
}
$this->displayCircles($circles);
return 0;
}
/**
* @param Circle[] $circles
*/
private function displayCircles(array $circles): void {
$output = new ConsoleOutput();
$output = $output->section();
$table = new Table($output);
$table->setHeaders(
['Single Id', 'Name', 'Config', 'Source', 'Owner', 'Instance', 'Population']
);
$table->render();
$displayName = $this->input->getOption('display-name');
$display = ($this->input->getOption('def') ? Circle::FLAGS_LONG : Circle::FLAGS_SHORT);
foreach ($circles as $circle) {
$owner = $circle->getOwner();
$table->appendRow(
[
$circle->getSingleId(),
$this->cut(($displayName ? $circle->getDisplayName() : $circle->getName()), 40),
json_encode(Circle::getCircleFlags($circle, $display)),
Circle::$DEF_SOURCE[$circle->getSource()],
$this->cut($displayName ? $owner->getDisplayName() : $owner->getUserId(), 40),
$this->configService->displayInstance($owner->getInstance()),
$circle->getPopulation() . '/'
. $this->getInt('members_limit', $circle->getSettings(), -1)
. ' (' . $circle->getPopulationInherited() . ')'
]
);
}
}
}
@@ -0,0 +1,171 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\Db\CoreRequestBuilder;
use OCA\Circles\Exceptions\MaintenanceException;
use OCA\Circles\Service\MaintenanceService;
use OCA\Circles\Service\OutputService;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Class CirclesMaintenance
*
* @package OCA\Circles\Command
*/
class CirclesMaintenance extends Base {
/** @var CoreRequestBuilder */
private $coreRequestBuilder;
/** @var MaintenanceService */
private $maintenanceService;
/** @var OutputService */
private $outputService;
/**
* CirclesMaintenance constructor.
*
* @param CoreRequestBuilder $coreRequestBuilder
* @param MaintenanceService $maintenanceService
* @param OutputService $outputService
*/
public function __construct(
CoreRequestBuilder $coreRequestBuilder,
MaintenanceService $maintenanceService,
OutputService $outputService
) {
parent::__construct();
$this->coreRequestBuilder = $coreRequestBuilder;
$this->maintenanceService = $maintenanceService;
$this->outputService = $outputService;
}
protected function configure() {
parent::configure();
$this->setName('circles:maintenance')
->setDescription('Clean stuff, keeps the app running')
->addOption('level', '', InputOption::VALUE_REQUIRED, 'level of maintenance', '3')
->addOption(
'reset', '', InputOption::VALUE_NONE, 'reset Circles; remove all data related to the App'
)
->addOption(
'clean-shares', '', InputOption::VALUE_NONE, 'remove Circles\' shares'
)
->addOption(
'uninstall', '', InputOption::VALUE_NONE,
'Uninstall the apps and everything related to the app from the database'
)
->addOption('force-refresh', '', InputOption::VALUE_NONE, 'enforce some refresh');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$reset = $input->getOption('reset');
$uninstall = $input->getOption('uninstall');
$level = (int)$input->getOption('level');
if ($reset || $uninstall) {
$action = $uninstall ? 'uninstall' : 'reset';
$output->writeln('');
$output->writeln('');
$output->writeln(
'<error>WARNING! You are about to delete all data related to the Circles App!</error>'
);
$question = new ConfirmationQuestion(
'<comment>Do you really want to ' . $action . ' Circles ?</comment> (y/N) ', false,
'/^(y|Y)/i'
);
$helper = $this->getHelper('question');
if (!$helper->ask($input, $output, $question)) {
$output->writeln('aborted.');
return 0;
}
$output->writeln('');
$output->writeln('<error>WARNING! This operation is not reversible.</error>');
$question = new Question(
'<comment>Please confirm this destructive operation by typing \'' . $action
. '\'</comment>: ', ''
);
$helper = $this->getHelper('question');
$confirmation = $helper->ask($input, $output, $question);
if (strtolower($confirmation) !== $action) {
$output->writeln('aborted.');
return 0;
}
$this->coreRequestBuilder->cleanDatabase($input->getOption('clean-shares'));
if ($uninstall) {
$this->coreRequestBuilder->uninstall();
}
$output->writeln('<info>' . $action . ' done</info>');
return 0;
}
$this->outputService->setOccOutput($output);
$this->maintenanceService->setOccOutput($output);
for ($i = 1; $i <= $level; $i++) {
try {
$this->maintenanceService->runMaintenance($i, $input->getOption('force-refresh'));
} catch (MaintenanceException $e) {
}
}
$output->writeln('');
$output->writeln('<info>done</info>');
return 0;
}
}
@@ -0,0 +1,404 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use Exception;
use OC\Core\Command\Base;
use OCA\Circles\Db\CircleRequest;
use OCA\Circles\Db\MemberRequest;
use OCA\Circles\Db\MembershipRequest;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\FederatedUser;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\MembershipService;
use OCA\Circles\Tools\Exceptions\InvalidItemException;
use OCA\Circles\Tools\Exceptions\ItemNotFoundException;
use OCA\Circles\Tools\Exceptions\UnknownTypeException;
use OCA\Circles\Tools\Model\SimpleDataStore;
use OCA\Circles\Tools\Model\TreeNode;
use OCA\Circles\Tools\Traits\TArrayTools;
use OCA\Circles\Tools\Traits\TConsoleTree;
use OCP\IUserManager;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CirclesMemberships
*
* @package OCA\Circles\Command
*/
class CirclesMemberships extends Base {
use TArrayTools;
use TConsoleTree;
/** @var IUserManager */
private $userManager;
/** @var MembershipRequest */
private $membershipRequest;
/** @var MemberRequest */
private $memberRequest;
/** @var CircleRequest */
private $circleRequest;
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/** @var MembershipService */
private $membershipService;
/** @var ConfigService */
private $configService;
/** @var InputInterface */
private $input;
/** @var array */
private $memberships = [];
/**
* CirclesMemberships constructor.
*
* @param IUserManager $userManager
* @param MembershipRequest $membershipRequest
* @param MemberRequest $memberRequest
* @param CircleRequest $circleRequest
* @param FederatedUserService $federatedUserService
* @param CircleService $circleService
* @param MembershipService $membershipService
* @param ConfigService $configService
*/
public function __construct(
IUserManager $userManager,
MembershipRequest $membershipRequest,
MemberRequest $memberRequest,
CircleRequest $circleRequest,
FederatedUserService $federatedUserService,
CircleService $circleService,
MembershipService $membershipService,
ConfigService $configService
) {
parent::__construct();
$this->userManager = $userManager;
$this->memberRequest = $memberRequest;
$this->membershipRequest = $membershipRequest;
$this->circleRequest = $circleRequest;
$this->federatedUserService = $federatedUserService;
$this->circleService = $circleService;
$this->membershipService = $membershipService;
$this->configService = $configService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:memberships')
->setDescription('index and display memberships for local and federated users')
->addArgument('userId', InputArgument::OPTIONAL, 'userId to generate memberships', '')
->addOption('display-name', '', InputOption::VALUE_NONE, 'display the displayName')
// ->addOption('reset', '', InputOption::VALUE_NONE, 'reset memberships')
->addOption('all', '', InputOption::VALUE_NONE, 'refresh memberships for all entities')
->addOption(
'type', '', InputOption::VALUE_REQUIRED, 'type of the user',
Member::$TYPE[Member::TYPE_SINGLE]
);
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws CircleNotFoundException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
* @throws FederatedItemException
* @throws InvalidIdException
* @throws SingleCircleNotFoundException
* @throws Exception
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$this->input = $input;
if ($input->getOption('all')) {
$this->manageAllMemberships();
return 0;
}
$userId = $input->getArgument('userId');
if ($userId === '') {
throw new Exception('Not enough arguments (missing: "userId").');
}
$type = Member::parseTypeString($input->getOption('type'));
$federatedUser = $this->federatedUserService->getFederatedUser($userId, $type);
// if ($this->input->getOption('reset')) {
// $this->membershipsService->resetMemberships($federatedUser->getSingleId());
//
// return 0;
// }
$output->writeln('Id: <info>' . $federatedUser->getUserId() . '</info>');
$output->writeln('Instance: <info>' . $federatedUser->getInstance() . '</info>');
$output->writeln('Type: <info>' . Member::$TYPE[$federatedUser->getUserType()] . '</info>');
$output->writeln('SingleId: <info>' . $federatedUser->getSingleId() . '</info>');
$output->writeln('');
$output->writeln('Memberships:');
$count = $this->membershipService->manageMemberships($federatedUser->getSingleId());
if ($count === 0) {
$output->writeln('(database not updated)');
} else {
$output->writeln('(' . $count . ' entries generated/updated in the database)');
}
foreach ($federatedUser->getMemberships() as $membership) {
$this->memberships[$membership->getCircleId()] = $membership;
$output->writeln(
'- <info>' . $membership->getCircleId() . '</info> ('
. Member::$DEF_LEVEL[$membership->getLevel()] . ')'
);
}
$output->writeln('');
$tree = new TreeNode(null, new SimpleDataStore(['federatedUser' => $federatedUser]));
$this->generateTree($federatedUser->getSingleId(), $tree);
$this->drawTree(
$tree, [$this, 'displayLeaf'],
[
'height' => 3,
'node-spacing' => 0,
'item-spacing' => 1,
]
);
return 0;
}
/**
* @param string $id
* @param TreeNode $tree
* @param array $knownIds
*/
private function generateTree(string $id, TreeNode $tree, array $knownIds = []) {
if (in_array($id, $knownIds)) {
return;
}
$knownIds[] = $id;
$members = $this->memberRequest->getMembersBySingleId($id);
foreach ($members as $member) {
if ($member->getLevel() < Member::LEVEL_MEMBER
|| $member->getCircle()->isConfig(Circle::CFG_SINGLE)) {
continue;
}
$item = new TreeNode(
$tree, new SimpleDataStore(
[
'member' => $member,
'cycling' => in_array($member->getCircleId(), $knownIds)
]
)
);
$this->generateTree($member->getCircleId(), $item, $knownIds);
}
}
/**
* @param SimpleDataStore $data
* @param int $lineNumber
*
* @return string
* @throws OwnerNotFoundException
*/
public function displayLeaf(SimpleDataStore $data, int $lineNumber): string {
if ($lineNumber === 3) {
return ($data->gBool('cycling')) ? '<comment>(loop detected)</comment>' : '';
}
try {
$line = '';
if ($data->hasKey('federatedUser')) {
/** @var FederatedUser $federatedUser */
$federatedUser = $data->gObj('federatedUser', FederatedUser::class);
if ($lineNumber === 2) {
return '';
}
$line .= '<info>' . $federatedUser->getSingleId() . '</info>';
if (!$this->configService->isLocalInstance($federatedUser->getInstance())) {
$line .= '@' . $federatedUser->getInstance();
}
return $line;
}
if ($data->hasKey('member')) {
/** @var Member $member */
$member = $data->gObj('member', Member::class);
$circle = $member->getCircle();
if ($lineNumber === 1) {
$line .= '<info>' . $circle->getSingleId() . '</info>';
if (!$this->configService->isLocalInstance($circle->getInstance())) {
$line .= '@' . $circle->getInstance();
}
$line .= ' (' . ($this->input->getOption('display-name') ?
$circle->getDisplayName() : $circle->getName()) . ')';
$line .= ' <info>MemberId</info>: ' . $member->getId();
$line .= ' <info>Level</info>: ' . Member::$DEF_LEVEL[$member->getLevel()];
$knownMembership = $this->memberships[$member->getCircleId()];
if ($member->getLevel() !== $knownMembership->getLevel()) {
$line .= ' (' . Member::$DEF_LEVEL[$knownMembership->getLevel()] . ')';
}
}
if ($lineNumber === 2) {
$owner = $circle->getOwner();
$line .= '<info>Owner</info>: ' . $owner->getUserId() . '@' . $owner->getInstance() . ' ';
if ($owner->hasBasedOn()) {
$line .= '(' . Circle::$DEF_SOURCE[$owner->getBasedOn()->getSource()] . ') ';
}
$type = implode(", ", Circle::getCircleFlags($circle, Circle::FLAGS_LONG));
$line .= ($type === '') ? '' : '<info>Config</info>: ' . $type;
}
return $line;
}
} catch (InvalidItemException | ItemNotFoundException | UnknownTypeException $e) {
}
return '';
}
/**
* @throws FederatedUserNotFoundException
* @throws InitiatorNotFoundException
* @throws OwnerNotFoundException
* @throws RequestBuilderException
*/
private function manageAllMemberships() {
// if ($this->input->getOption('reset')) {
// $this->membershipsService->resetMemberships('', true);
//
// return;
// }
$this->federatedUserService->bypassCurrentUserCondition(true);
$probe = new CircleProbe();
$probe->includeSystemCircles()
->includeSingleCircles()
->includePersonalCircles();
$circles = $this->circleService->getCircles($probe);
$output = new ConsoleOutput();
$output = $output->section();
$table = new Table($output);
$table->setHeaders(['Circle Id', 'Name', 'Source', 'Owner', 'Instance', 'Updated', 'Memberships']);
$table->render();
$count = 0;
foreach ($circles as $circle) {
$owner = $circle->getOwner();
$updated = $this->membershipService->manageMemberships($circle->getSingleId());
$count += $updated;
$federatedUser = $this->circleRequest->getFederatedUserBySingleId($circle->getSingleId());
$table->appendRow(
[
$circle->getSingleId(),
$circle->getDisplayName(),
($circle->getSource() > 0) ? Circle::$DEF_SOURCE[$circle->getSource()] : '',
$owner->getUserId(),
$this->configService->displayInstance($owner->getInstance()),
$updated,
sizeof($federatedUser->getMemberships())
]
);
}
$output->writeln($count . ' memberships updated');
}
}
@@ -0,0 +1,534 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OCA\Circles\Tools\Exceptions\RequestNetworkException;
use OCA\Circles\Tools\Exceptions\SignatoryException;
use OCA\Circles\Tools\Exceptions\SignatureException;
use OCA\Circles\Tools\Model\NCRequest;
use OCA\Circles\Tools\Model\NCSignedRequest;
use OCA\Circles\Tools\Traits\TNCWellKnown;
use OCA\Circles\Tools\Traits\TStringTools;
use Exception;
use OC\Core\Command\Base;
use OCA\Circles\AppInfo\Application;
use OCA\Circles\Db\RemoteRequest;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteUidException;
use OCA\Circles\Model\Federated\RemoteInstance;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\GlobalScaleService;
use OCA\Circles\Service\InterfaceService;
use OCA\Circles\Service\RemoteStreamService;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
/**
* Class CirclesRemote
*
* @package OCA\Circles\Command
*/
class CirclesRemote extends Base {
use TNCWellKnown;
use TStringTools;
/** @var RemoteRequest */
private $remoteRequest;
/** @var GlobalScaleService */
private $globalScaleService;
/** @var RemoteStreamService */
private $remoteStreamService;
/** @var InterfaceService */
private $interfaceService;
/** @var ConfigService */
private $configService;
/** @var InputInterface */
private $input;
/** @var OutputInterface */
private $output;
/**
* CirclesRemote constructor.
*
* @param RemoteRequest $remoteRequest
* @param GlobalScaleService $globalScaleService
* @param RemoteStreamService $remoteStreamService
* @param InterfaceService $interfaceService
* @param ConfigService $configService
*/
public function __construct(
RemoteRequest $remoteRequest,
GlobalScaleService $globalScaleService,
RemoteStreamService $remoteStreamService,
InterfaceService $interfaceService,
ConfigService $configService
) {
parent::__construct();
$this->remoteRequest = $remoteRequest;
$this->globalScaleService = $globalScaleService;
$this->remoteStreamService = $remoteStreamService;
$this->interfaceService = $interfaceService;
$this->configService = $configService;
$this->setup('app', 'circles');
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:remote')
->setDescription('remote features')
->addArgument('host', InputArgument::OPTIONAL, 'host of the remote instance of Nextcloud')
->addOption(
'type', '', InputOption::VALUE_REQUIRED, 'set type of remote', RemoteInstance::TYPE_UNKNOWN
)
->addOption(
'iface', '', InputOption::VALUE_REQUIRED, 'set interface to use to contact remote',
InterfaceService::$LIST_IFACE[InterfaceService::IFACE_FRONTAL]
)
->addOption('yes', '', InputOption::VALUE_NONE, 'silently add the remote instance')
->addOption('all', '', InputOption::VALUE_NONE, 'display all information');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws Exception
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$host = $input->getArgument('host');
$this->input = $input;
$this->output = $output;
if ($host) {
$this->requestInstance($host);
} else {
$this->checkKnownInstance();
}
return 0;
}
/**
* @param string $host
*
* @throws Exception
*/
private function requestInstance(string $host): void {
$remoteType = $this->getRemoteType();
$remoteIface = $this->getRemoteInterface();
$this->interfaceService->setCurrentInterface($remoteIface);
$webfinger = $this->getWebfinger($host, Application::APP_SUBJECT);
if ($this->input->getOption('all')) {
$this->output->writeln('- Webfinger on <info>' . $host . '</info>');
$this->output->writeln(json_encode($webfinger, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
$this->output->writeln('');
}
if ($this->input->getOption('all')) {
$circleLink = $this->extractLink(Application::APP_REL, $webfinger);
$this->output->writeln('- Information about Circles app on <info>' . $host . '</info>');
$this->output->writeln(json_encode($circleLink, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
$this->output->writeln('');
}
$this->output->writeln('- Available services on <info>' . $host . '</info>');
foreach ($webfinger->getLinks() as $link) {
$app = $link->getProperty('name');
$ver = $link->getProperty('version');
if ($app !== '') {
$app .= ' ';
}
if ($ver !== '') {
$ver = 'v' . $ver;
}
$this->output->writeln(' * ' . $link->getRel() . ' ' . $app . $ver);
}
$this->output->writeln('');
$this->output->writeln('- Resources related to Circles on <info>' . $host . '</info>');
$resource = $this->getResourceData($host, Application::APP_SUBJECT, Application::APP_REL);
$this->output->writeln(json_encode($resource, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
$this->output->writeln('');
$tempUid = $resource->g('uid');
$this->output->writeln(
'- Confirming UID=' . $tempUid . ' from parsed Signatory at <info>' . $host . '</info>'
);
try {
/** @var RemoteInstance $remoteSignatory */
$remoteSignatory = $this->remoteStreamService->retrieveSignatory($resource->g('id'), true);
$this->output->writeln(' * No SignatureException: <info>Identity authed</info>');
} catch (SignatureException $e) {
$this->output->writeln(
'<error>' . $host . ' cannot auth its identity: ' . $e->getMessage() . '</error>'
);
return;
}
$this->output->writeln(' * Found <info>' . $remoteSignatory->getUid() . '</info>');
if ($remoteSignatory->getUid(true) !== $tempUid) {
$this->output->writeln('<error>looks like ' . $host . ' is faking its identity');
return;
}
$this->output->writeln('');
$testUrl = $resource->g('test');
$this->output->writeln('- Testing signed payload on <info>' . $testUrl . '</info>');
try {
$localSignatory = $this->remoteStreamService->getAppSignatory();
} catch (SignatoryException $e) {
$this->output->writeln(
'<error>Federated Circles not enabled locally. Please run ./occ circles:remote:init</error>'
);
return;
}
$payload = [
'test' => 42,
'token' => $this->uuid()
];
$signedRequest = $this->outgoingTest($testUrl, $payload);
$this->output->writeln(' * Payload: ');
$this->output->writeln(json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
$this->output->writeln('');
$this->output->writeln(' * Clear Signature: ');
$this->output->writeln('<comment>' . $signedRequest->getClearSignature() . '</comment>');
$this->output->writeln('');
$this->output->writeln(' * Signed Signature (base64 encoded): ');
$this->output->writeln(
'<comment>' . base64_encode($signedRequest->getSignedSignature()) . '</comment>'
);
$this->output->writeln('');
$result = $signedRequest->getOutgoingRequest()->getResult();
$code = $result->getStatusCode();
$this->output->writeln(' * Result: ' . (($code === 200) ? '<info>' . $code . '</info>' : $code));
$this->output->writeln(
json_encode(json_decode($result->getContent(), true), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
);
$this->output->writeln('');
if ($this->input->getOption('all')) {
$this->output->writeln('');
$this->output->writeln('<info>### Complete report ###</info>');
$this->output->writeln(json_encode($signedRequest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
$this->output->writeln('');
}
if ($remoteSignatory->getUid() !== $localSignatory->getUid()) {
$remoteSignatory->setInstance($host)
->setType($remoteType)
->setInterface($remoteIface);
try {
$stored = new RemoteInstance();
$this->remoteStreamService->confirmValidRemote($remoteSignatory, $stored);
$this->output->writeln(
'<info>The remote instance ' . $host
. ' is already known with this current identity</info>'
);
$this->output->writeln('- updating item');
$this->remoteStreamService->update($remoteSignatory, RemoteStreamService::UPDATE_ITEM);
if ($remoteSignatory->getType() !== $stored->getType()) {
$this->output->writeln(
'- updating type from ' . $stored->getType() . ' to '
. $remoteSignatory->getType()
);
$this->remoteStreamService->update(
$remoteSignatory, RemoteStreamService::UPDATE_TYPE
);
}
if ($remoteSignatory->getInstance() !== $stored->getInstance()) {
$this->output->writeln(
'- updating host from ' . $stored->getInstance() . ' to '
. $remoteSignatory->getInstance()
);
$this->remoteStreamService->update(
$remoteSignatory, RemoteStreamService::UPDATE_INSTANCE
);
}
if ($remoteSignatory->getId() !== $stored->getId()) {
$this->output->writeln(
'- updating href/Id from ' . $stored->getId() . ' to '
. $remoteSignatory->getId()
);
$this->remoteStreamService->update($remoteSignatory, RemoteStreamService::UPDATE_HREF);
}
} catch (RemoteUidException $e) {
$this->updateRemote($remoteSignatory);
} catch (RemoteNotFoundException $e) {
$this->saveRemote($remoteSignatory);
}
}
}
/**
* @param RemoteInstance $remoteSignatory
*
* @throws RemoteUidException
*/
private function saveRemote(RemoteInstance $remoteSignatory) {
$this->output->writeln('');
$helper = $this->getHelper('question');
$this->output->writeln(
'The remote instance <info>' . $remoteSignatory->getInstance() . '</info> looks good.'
);
$question = new ConfirmationQuestion(
'Would you like to identify this remote instance as \'<comment>' . $remoteSignatory->getType()
. '</comment>\' using interface \'<comment>'
. InterfaceService::$LIST_IFACE[$remoteSignatory->getInterface()]
. '</comment>\' ? (y/N) ',
false,
'/^(y|Y)/i'
);
if ($this->input->getOption('yes') || $helper->ask($this->input, $this->output, $question)) {
if (!$this->interfaceService->isInterfaceInternal($remoteSignatory->getInterface())) {
$remoteSignatory->setAliases([]);
}
$this->remoteRequest->save($remoteSignatory);
$this->output->writeln('<info>remote instance saved</info>');
}
}
/**
* @param RemoteInstance $remoteSignatory
*
* @throws RemoteUidException
*/
private function updateRemote(RemoteInstance $remoteSignatory): void {
$this->output->writeln('');
$helper = $this->getHelper('question');
$this->output->writeln(
'The remote instance <info>' . $remoteSignatory->getInstance()
. '</info> is known but <error>its identity has changed.</error>'
);
$this->output->writeln(
'<comment>If you are not sure on why identity changed, please say No to the next question and contact the admin of the remote instance</comment>'
);
$question = new ConfirmationQuestion(
'Do you consider this new identity as valid and update the entry in the database? (y/N) ',
false,
'/^(y|Y)/i'
);
if ($helper->ask($this->input, $this->output, $question)) {
$this->remoteStreamService->update($remoteSignatory);
$this->output->writeln('remote instance updated');
}
}
/**
* @param string $remote
* @param array $payload
*
* @return NCSignedRequest
* @throws RequestNetworkException
* @throws SignatoryException
*/
private function outgoingTest(string $remote, array $payload): NCSignedRequest {
$request = new NCRequest();
$request->basedOnUrl($remote);
$request->setFollowLocation(true);
$request->setLocalAddressAllowed(true);
$request->setTimeout(5);
$request->setData($payload);
$app = $this->remoteStreamService->getAppSignatory();
$signedRequest = $this->remoteStreamService->signOutgoingRequest($request, $app);
$outgoingRequest = $signedRequest->getOutgoingRequest();
$outgoingRequest->setLocalAddressAllowed(true);
$outgoingRequest->setFollowLocation(true);
$this->doRequest($outgoingRequest);
return $signedRequest;
}
/**
*
*/
private function checkKnownInstance(): void {
$this->verifyGSInstances();
$this->checkRemoteInstances();
}
/**
*
*/
private function verifyGSInstances(): void {
$instances = $this->globalScaleService->getGlobalScaleInstances();
$known = array_map(
function (RemoteInstance $instance): string {
return $instance->getInstance();
}, $this->remoteRequest->getFromType(RemoteInstance::TYPE_GLOBALSCALE)
);
$missing = array_diff($instances, $known);
foreach ($missing as $instance) {
$this->syncGSInstance($instance);
}
}
/**
* @param string $instance
*/
private function syncGSInstance(string $instance): void {
$this->output->write('Adding <comment>' . $instance . '</comment>: ');
if ($this->configService->isLocalInstance($instance)) {
$this->output->writeln('<comment>instance is local</comment>');
return;
}
try {
$this->remoteStreamService->addRemoteInstance(
$instance,
RemoteInstance::TYPE_GLOBALSCALE,
InterfaceService::IFACE_INTERNAL,
true
);
$this->output->writeln('<info>ok</info>');
} catch (Exception $e) {
$msg = ($e->getMessage() === '') ? '' : ' (' . $e->getMessage() . ')';
$this->output->writeln('<error>' . get_class($e) . $msg . '</error>');
}
}
private function checkRemoteInstances(): void {
$instances = $this->remoteRequest->getAllInstances();
$output = new ConsoleOutput();
$output = $output->section();
$table = new Table($output);
$table->setHeaders(['Instance', 'Type', 'iface', 'UID', 'Authed', 'Aliases']);
$table->render();
foreach ($instances as $instance) {
try {
$current = $this->remoteStreamService->retrieveRemoteInstance($instance->getInstance());
if ($current->getUid(true) === $instance->getUid(true)) {
$currentUid = '<info>' . $current->getUid(true) . '</info>';
} else {
$currentUid = '<error>' . $current->getUid(true) . '</error>';
}
} catch (Exception $e) {
$currentUid = '<error>' . $e->getMessage() . '</error>';
}
$table->appendRow(
[
$instance->getInstance(),
$instance->getType(),
InterfaceService::$LIST_IFACE[$instance->getInterface()],
$instance->getUid(),
$currentUid,
json_encode($instance->getAliases())
]
);
}
}
/**
* @throws Exception
*/
private function getRemoteType(): string {
foreach (RemoteInstance::$LIST_TYPE as $type) {
if (strtolower($this->input->getOption('type')) === strtolower($type)) {
return $type;
}
}
throw new Exception('Unknown type: ' . implode(', ', RemoteInstance::$LIST_TYPE));
}
/**
* @throws Exception
*/
private function getRemoteInterface(): int {
foreach (InterfaceService::$LIST_IFACE as $iface => $def) {
if (strtolower($this->input->getOption('iface')) === strtolower($def)) {
return $iface;
}
}
throw new Exception('Unknown interface: ' . implode(', ', InterfaceService::$LIST_IFACE));
}
}
@@ -0,0 +1,433 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OCA\Circles\Tools\Exceptions\InvalidItemException;
use OCA\Circles\Tools\Traits\TDeserialize;
use OCA\Circles\Tools\Traits\TArrayTools;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\UnknownInterfaceException;
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\Membership;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Model\Report;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\InterfaceService;
use OCA\Circles\Service\MemberService;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CirclesReport
*
* @package OCA\Circles\Command
*/
class CirclesReport extends Base implements IInteractiveShellClient {
use TDeserialize;
use TArrayTools;
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/** @var MemberService */
private $memberService;
/** @var InterfaceService */
private $interfaceService;
/** @var ConfigService */
private $configService;
/** @var OutputInterface */
private $output;
/** @var Report */
private $report;
/**
* CirclesReport constructor.
*
* @param FederatedUserService $federatedUserService
* @param CircleService $circleService
* @param MemberService $memberService
* @param ConfigService $configService
*/
public function __construct(
FederatedUserService $federatedUserService,
CircleService $circleService,
MemberService $memberService,
InterfaceService $interfaceService,
ConfigService $configService
) {
parent::__construct();
$this->federatedUserService = $federatedUserService;
$this->circleService = $circleService;
$this->memberService = $memberService;
$this->interfaceService = $interfaceService;
$this->configService = $configService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:report')
->setDescription('Read and write obfuscated report')
->addOption('local', '', InputOption::VALUE_NONE, 'Use local report')
->addOption('read', '', InputOption::VALUE_REQUIRED, 'File containing the report to read', '');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws InvalidItemException
* @throws InitiatorNotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
throw new \Exception('not available');
$filename = $input->getOption('read');
$local = $input->getOption('local');
$this->output = $output;
$report = null;
if ($filename === '' || $local) {
$report = $this->generateReport();
if (!$local) {
$this->output->writeln(json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
return 0;
}
}
if ($filename !== '') {
/** @var Report $report */
$data = file_get_contents($filename);
$report = $this->deserialize(json_decode($data, true), Report::class);
}
if (!is_null($report)) {
$this->readReport($input, $report);
}
return 0;
}
/**
* @throws InitiatorNotFoundException
* @throws UnknownInterfaceException
*/
private function generateReport(): Report {
$report = new Report();
$report->setSource($this->interfaceService->getLocalInstance());
$this->federatedUserService->bypassCurrentUserCondition(true);
$probe = new CircleProbe();
$probe->includeSystemCircles();
$raw = $this->circleService->getCircles($probe);
$circles = [];
foreach ($raw as $circle) {
$circle->getMembers();
$circles[] = $this->obfuscateCircle($circle);
}
$report->setCircles($circles);
return $report;
}
/**
* @param InputInterface $input
* @param Report $report
*/
private function readReport(InputInterface $input, Report $report) {
$output = new ConsoleOutput();
$this->output = $output->section();
$this->report = $report;
$interactiveShell = new NC22InteractiveShell($this, $input, $this);
$commands = [
'circles.list',
'circles.delete.#circleId',
'members.list.#circleId',
'members.details.#memberId',
'remoteInstance.list'
];
$interactiveShell->setCommands($commands);
$interactiveShell->run();
}
/**
* @param string $source
* @param string $field
*
* @return string[]
*/
public function fillCommandList(string $source, string $field): array {
echo $source . ' ' . $field . "\n";
return ['abcd', 'abdde', 'erfg'];
}
/**
* @param string $command
*/
public function manageCommand(string $command): void {
// echo $command . "\n";
}
/**
* @param Circle $circle
*
* @return Circle
*/
private function obfuscateCircle(Circle $circle): Circle {
$singleId = $this->obfuscateId($circle->getSingleId());
$circle->setSingleId($singleId)
->setName($singleId)
->setDisplayName($singleId)
->setDescription('')
->setCreation(0);
if ($circle->hasOwner()) {
$circle->setOwner($this->obfuscateMember($circle->getOwner()));
}
if ($circle->hasInitiator()) {
$circle->setInitiator($this->obfuscateMember($circle->getInitiator()));
}
if ($circle->hasMembers()) {
$members = [];
foreach ($circle->getMembers() as $member) {
$members[] = $this->obfuscateMember($member);
}
$circle->setMembers($members);
}
if ($circle->hasMemberships()) {
$memberships = [];
foreach ($circle->getMemberships() as $membership) {
$memberships[] = $this->obfuscateMembership($membership);
}
$circle->setMemberships($memberships);
}
return $circle;
}
/**
* @param Member $member
*
* @return Member
*/
private function obfuscateMember(Member $member): Member {
$memberId = $this->obfuscateId($member->getId());
$singleId = $this->obfuscateId($member->getSingleId());
$circleId = $this->obfuscateId($member->getCircleId());
$member->setSingleId($singleId)
->setCircleId($circleId)
->setId($memberId)
->setUserId($singleId)
->setDisplayName($singleId)
->setDisplayUpdate(0)
->setNotes('')
->setContactId('')
->setContactMeta('')
->setJoined(0);
if ($member->hasCircle()) {
$member->setCircle($this->obfuscateCircle($member->getCircle()));
}
if ($member->hasBasedOn()) {
$member->setBasedOn($this->obfuscateCircle($member->getBasedOn()));
}
if ($member->hasInheritedBy()) {
$member->setInheritedBy($this->obfuscateFederatedUser($member->getInheritedBy()));
}
if ($member->hasInheritanceFrom()) {
$member->setInheritanceFrom($this->obfuscateMember($member->getInheritanceFrom()));
}
if ($member->hasRemoteInstance()) {
$member->setRemoteInstance($this->obfuscateRemoteInstance($member->getRemoteInstance()));
}
if ($member->hasMemberships()) {
$memberships = [];
foreach ($member->getMemberships() as $membership) {
$memberships[] = $this->obfuscateMembership($membership);
}
$member->setMemberships($memberships);
}
return $member;
}
/**
* @param FederatedUser $federatedUser
*
* @return FederatedUser
*/
private function obfuscateFederatedUser(FederatedUser $federatedUser): FederatedUser {
$singleId = $this->obfuscateId($federatedUser->getSingleId());
$federatedUser->setSingleId($singleId)
->setUserId($singleId);
if ($federatedUser->hasBasedOn()) {
$federatedUser->setBasedOn($this->obfuscateCircle($federatedUser->getBasedOn()));
}
// what was that for ?
// if ($federatedUser->hasLink()) {
// $federatedUser->setLink($this->obfuscateMembership($federatedUser->getLink()));
// }
if ($federatedUser->hasMemberships()) {
$memberships = [];
foreach ($federatedUser->getMemberships() as $membership) {
$memberships[] = $this->obfuscateMembership($membership);
}
$federatedUser->setMemberships($memberships);
}
return $federatedUser;
}
/**
* @param Membership $membership
*
* @return Membership
*/
private function obfuscateMembership(Membership $membership): Membership {
$membership->setSingleId($this->obfuscateId($membership->getSingleId()));
$membership->setCircleId($this->obfuscateId($membership->getCircleId()));
$membership->setInheritanceFirst($this->obfuscateId($membership->getInheritanceFirst()));
$membership->setInheritanceLast($this->obfuscateId($membership->getInheritanceLast()));
$path = [];
foreach ($membership->getInheritancePath() as $item) {
$path[] = $this->obfuscateId($item);
}
$membership->setInheritancePath($path);
return $membership;
}
/**
* @param RemoteInstance $remoteInstance
*
* @return RemoteInstance
*/
private function obfuscateRemoteInstance(RemoteInstance $remoteInstance): RemoteInstance {
return $remoteInstance;
}
/**
* @param string $id
*
* @return string
*/
private function obfuscateId(string $id): string {
return substr($id, 0, 5) . '.' . md5(substr($id, 5));
}
/**
* @param NC22InteractiveShellSession $session
*/
public function onNewPrompt(NC22InteractiveShellSession $session): void {
$prompt =
'Circles Report [<info>' . $this->report->getSource() . '</info>]:<comment>%PATH%</comment>';
$commands = [];
if ($session->getData()->g('currentStatus') === 'write') {
$commands[] = 'cancel';
$commands[] = 'write';
$prompt .= '<error>#</error> ';
} else {
$commands[] = 'edit';
$prompt .= '$ ';
}
$session->setGlobalCommands($commands)
->setPrompt($prompt);
}
/**
* @param NC22InteractiveShellSession $session
* @param $command
*/
public function onNewCommand(NC22InteractiveShellSession $session, $command): void {
echo $session->getPath();
}
}
@@ -0,0 +1,211 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedEventException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotConfirmedException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Helpers\MemberHelper;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCP\Security\IHasher;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class CirclesSetting extends Base {
private IHasher $hasher;
private FederatedUserService $federatedUserService;
private CircleService $circleService;
private ConfigService $configService;
public function __construct(
IHasher $hasher,
FederatedUserService $federatedUserService,
CircleService $circlesService,
ConfigService $configService
) {
parent::__construct();
$this->hasher = $hasher;
$this->federatedUserService = $federatedUserService;
$this->circleService = $circlesService;
$this->configService = $configService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:manage:setting')
->setDescription('edit setting for a Circle')
->addArgument('circle_id', InputArgument::REQUIRED, 'ID of the circle')
->addArgument('setting', InputArgument::OPTIONAL, 'setting to edit', '')
->addArgument('value', InputArgument::OPTIONAL, 'value', '')
->addOption('unset', '', InputOption::VALUE_NONE, 'unset the setting')
->addOption(
'test-password', '', InputOption::VALUE_REQUIRED,
'test and compare password with hash (in case of static password)'
)
->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
->addOption('initiator-type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0')
->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws FederatedEventException
* @throws FederatedItemException
* @throws InitiatorNotFoundException
* @throws RequestBuilderException
* @throws CircleNotFoundException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InitiatorNotConfirmedException
* @throws InvalidIdException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws SingleCircleNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$circleId = (string)$input->getArgument('circle_id');
$setting = (string)$input->getArgument('setting');
$value = (string)$input->getArgument('value');
try {
$this->federatedUserService->commandLineInitiator(
$input->getOption('initiator'),
Member::parseTypeString($input->getOption('initiator-type')),
$circleId,
false
);
if ($setting === '') {
$circle = $this->circleService->getCircle($circleId);
$initiatorHelper = new MemberHelper($circle->getInitiator());
$initiatorHelper->mustBeAdmin();
$output->writeln(json_encode($circle->getSettings(), JSON_PRETTY_PRINT));
$testPassword = $input->getOption('test-password');
if ($testPassword !== null) {
$this->testPassword($output, $circle, $testPassword);
}
return 0;
}
if (!$input->getOption('unset') && $value === '') {
throw new InvalidArgumentException('you need to specify a value');
}
$outcome = $this->circleService->updateSetting(
$circleId,
$setting,
($input->getOption('unset')) ? null : $value,
);
} catch (FederatedItemException $e) {
if ($input->getOption('status-code')) {
throw new FederatedItemException(
' [' . get_class($e) . ', ' . $e->getStatus() . ']' . "\n" . $e->getMessage()
);
}
throw $e;
}
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($outcome, JSON_PRETTY_PRINT));
}
return 0;
}
private function testPassword(OutputInterface $output, Circle $circle, string $testPassword): void {
$output->writeln('');
$output->write('Password enforced for this Circle: ');
if (!$this->configService->enforcePasswordOnSharedFile($circle)) {
$output->writeln('<error>no</error>');
return;
}
$output->writeln('<info>yes</info>');
$output->write('Single password is configured for this Circle: ');
if (!$this->configService->isSinglePasswordAvailable($circle)) {
$output->writeln('<error>no</error>');
return;
}
$output->writeln('<info>yes</info>');
$output->write('Comparing password with hashed version in database: ');
if ($this->hasher->verify($testPassword, $circle->getSettings()['password_single'])) {
$output->writeln('<info>ok</info>');
} else {
$output->writeln('<error>fail</error>');
}
}
}
@@ -0,0 +1,181 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\ContactAddressBookNotFoundException;
use OCA\Circles\Exceptions\ContactFormatException;
use OCA\Circles\Exceptions\ContactNotFoundException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MigrationException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\MigrationService;
use OCA\Circles\Service\OutputService;
use OCA\Circles\Service\SyncService;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CirclesSync
*
* @package OCA\Circles\Command
*/
class CirclesSync extends Base {
/** @var SyncService */
private $syncService;
/** @var MigrationService */
private $migrationService;
/** @var OutputService */
private $outputService;
/** @var ConfigService */
private $configService;
/**
* CirclesSync constructor.
*
* @param SyncService $syncService
* @param OutputService $outputService
* @param ConfigService $configService
*/
public function __construct(
SyncService $syncService,
MigrationService $migrationService,
OutputService $outputService,
ConfigService $configService
) {
parent::__construct();
$this->syncService = $syncService;
$this->migrationService = $migrationService;
$this->outputService = $outputService;
$this->configService = $configService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:sync')
->setDescription('Sync Circles and Members')
->addOption('migration', '', InputOption::VALUE_NONE, 'Migrate from Circles 0.21.0')
->addOption('force', '', InputOption::VALUE_NONE, 'Force migration')
->addOption('force-run', '', InputOption::VALUE_NONE, 'Force migration run')
->addOption('apps', '', InputOption::VALUE_NONE, 'Sync Apps')
->addOption('users', '', InputOption::VALUE_NONE, 'Sync Nextcloud Account')
->addOption('groups', '', InputOption::VALUE_NONE, 'Sync Nextcloud Groups')
->addOption('contacts', '', InputOption::VALUE_NONE, 'Sync Contacts')
->addOption('remotes', '', InputOption::VALUE_NONE, 'Sync Remotes')
->addOption('global-scale', '', InputOption::VALUE_NONE, 'Sync GlobalScale');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws ContactAddressBookNotFoundException
* @throws ContactFormatException
* @throws ContactNotFoundException
* @throws FederatedUserException
* @throws InvalidIdException
* @throws MigrationException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$this->outputService->setOccOutput($output);
if ($input->getOption('migration')) {
if ($input->getOption('force-run')) {
$this->configService->setAppValue(ConfigService::MIGRATION_RUN, '0');
}
$this->migrationService->migration($input->getOption('force'));
$output->writeln('');
$output->writeln('Migration done');
return 0;
}
$output->writeln('<comment>This process requires a lot of memory.</comment>');
$output->writeln('<comment>If it crash, please restart it and it will continue where it stopped.</comment>');
$output->writeln('');
$sync = $this->filterSync($input);
$this->syncService->sync($sync);
$output->writeln('');
$output->writeln('Sync done');
return 0;
}
private function filterSync(InputInterface $input): int {
$sync = 0;
if ($input->getOption('apps')) {
$sync += SyncService::SYNC_APPS;
}
if ($input->getOption('users')) {
$sync += SyncService::SYNC_USERS;
}
if ($input->getOption('groups')) {
$sync += SyncService::SYNC_GROUPS;
}
if ($input->getOption('global-scale')) {
$sync += SyncService::SYNC_GLOBALSCALE;
}
if ($input->getOption('remotes')) {
$sync += SyncService::SYNC_REMOTES;
}
if ($input->getOption('contacts')) {
$sync += SyncService::SYNC_CONTACTS;
}
if ($sync === 0) {
$sync = SyncService::SYNC_ALL;
}
return $sync;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,148 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use Exception;
use OC\Core\Command\Base;
use OCA\Circles\Db\DeprecatedCirclesRequest;
use OCA\Circles\Exceptions\CommandMissingArgumentException;
use OCA\Circles\Exceptions\FakeException;
use OCP\IL10N;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class Groups extends Base {
/** @var IL10N */
private $l10n;
/** @var DeprecatedCirclesRequest */
private $circlesRequest;
/**
* Groups constructor.
*
* @param IL10N $l10n
* @param DeprecatedCirclesRequest $circlesRequest
*/
public function __construct(IL10N $l10n, DeprecatedCirclesRequest $circlesRequest) {
parent::__construct();
$this->l10n = $l10n;
$this->circlesRequest = $circlesRequest;
}
protected function configure() {
parent::configure();
$this->setName('circles:groups')
->setDescription('manage the linked groups')
->addOption('list', 'l', InputOption::VALUE_NONE, 'list all linked group')
->addOption('link', 'a', InputOption::VALUE_NONE, 'link a group to a circle')
->addOption('unlink', 'd', InputOption::VALUE_NONE, 'unlink a group from a circle')
->addArgument('circle_id', InputArgument::OPTIONAL, 'id of the circle')
->addArgument('group', InputArgument::OPTIONAL, 'name of the group');
}
protected function execute(InputInterface $input, OutputInterface $output) {
try {
$this->listLinkedGroups($input, $output);
$this->addLinkedGroups($input, $output);
$this->delLinkedGroups($input, $output);
} catch (FakeException $e) {
$output->writeln('done');
} catch (Exception $e) {
$output->writeln($e->getMessage());
}
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @throws FakeException
*/
private function listLinkedGroups(InputInterface $input, OutputInterface $output) {
if ($input->getOption('list') !== true) {
return ;
}
throw new FakeException();
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @throws FakeException
* @throws CommandMissingArgumentException
*/
private function addLinkedGroups(InputInterface $input, OutputInterface $output) {
if ($input->getOption('link') !== true) {
return;
}
[$circleId, $group] = $this->getCircleIdAndGroupFromArguments($input);
throw new FakeException();
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @throws FakeException
*/
private function delLinkedGroups(InputInterface $input, OutputInterface $output) {
if ($input->getOption('unlink') !== true) {
return;
}
[$circleId, $group] = $this->getCircleIdAndGroupFromArguments($input);
throw new FakeException();
}
private function getCircleIdAndGroupFromArguments(InputInterface $input) {
if ($input->getArgument('circle_id') === null
|| $input->getArgument('group') === null) {
throw new CommandMissingArgumentException(
);
// $this->l10n->t(
// 'Missing argument: {cmd} circle_id group', ['cmd' => './occ circles:link']
// )
}
return [$input->getArgument('circle_id'), $input->getArgument('group')];
}
}
@@ -0,0 +1,203 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OCA\Circles\Tools\Exceptions\RequestContentException;
use OCA\Circles\Tools\Exceptions\RequestNetworkException;
use OCA\Circles\Tools\Exceptions\RequestResultNotJsonException;
use OCA\Circles\Tools\Exceptions\RequestResultSizeException;
use OCA\Circles\Tools\Exceptions\RequestServerException;
use OCA\Circles\Tools\Model\NCRequest;
use OCA\Circles\Tools\Model\Request;
use Exception;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\GSStatusException;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\MemberService;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class MembersAdd
*
* @package OCA\Circles\Command
*/
class MembersAdd extends Base {
/** @var FederatedUserService */
private $federatedUserService;
/** @var MemberService */
private $memberService;
/** @var ConfigService */
private $configService;
/**
* MembersAdd constructor.
*
* @param FederatedUserService $federatedUserService
* @param MemberService $memberService
* @param ConfigService $configService
*/
public function __construct(
FederatedUserService $federatedUserService,
MemberService $memberService,
ConfigService $configService
) {
parent::__construct();
$this->federatedUserService = $federatedUserService;
$this->memberService = $memberService;
$this->configService = $configService;
}
protected function configure() {
parent::configure();
$this->setName('circles:members:add')
->setDescription('Add a member to a Circle')
->addArgument('circle_id', InputArgument::REQUIRED, 'ID of the circle')
->addArgument('user', InputArgument::REQUIRED, 'username of the member')
->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
->addOption('initiator-type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0')
->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception')
->addOption('type', '', InputOption::VALUE_REQUIRED, 'type of the user', '0');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws Exception
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$circleId = $input->getArgument('circle_id');
$userId = $input->getArgument('user');
$type = $input->getOption('type');
if ($type !== '0') {
$type = Member::parseTypeString($type);
}
try {
$this->federatedUserService->commandLineInitiator(
$input->getOption('initiator'),
Member::parseTypeString($input->getOption('initiator-type')),
$circleId,
false
);
$federatedUser = $this->federatedUserService->generateFederatedUser($userId, (int)$type);
$outcome = $this->memberService->addMember($circleId, $federatedUser);
} catch (FederatedItemException $e) {
if ($input->getOption('status-code')) {
throw new FederatedItemException(
' [' . get_class($e) . ', ' . $e->getStatus() . ']' . "\n" . $e->getMessage()
);
}
throw $e;
}
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($outcome, JSON_PRETTY_PRINT));
}
return 0;
}
/**
* @param string $search
* @param string $instance
*
* @return string
*/
private function findUserFromLookup(string $search, string &$instance = ''): string {
$userId = '';
/** @var string $lookup */
try {
$lookup = $this->configService->getGSLookup();
} catch (GSStatusException $e) {
return '';
}
$request = new NCRequest(ConfigService::GS_LOOKUP_USERS, Request::TYPE_GET);
$this->configService->configureRequest($request);
$request->basedOnUrl($lookup);
$request->addParam('search', $search);
try {
$users = $this->retrieveJson($request);
} catch (
RequestContentException |
RequestNetworkException |
RequestResultSizeException |
RequestServerException |
RequestResultNotJsonException $e
) {
return '';
}
$result = [];
foreach ($users as $user) {
if (!array_key_exists('userid', $user)) {
continue;
}
[, $host] = explode('@', $user['federationId']);
if (strtolower($user['userid']['value']) === strtolower($search)) {
$userId = $user['userid']['value'];
$instance = $host;
}
$result[] = $user['userid']['value'] . ' <info>@' . $host . '</info>';
}
// if ($userId === '') {
// foreach($result as $item) {
// $output->writeln($item);
// }
// }
return $userId;
}
}
@@ -0,0 +1,142 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\Db\MemberRequest;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\MemberService;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class MembersDetails
*
* @package OCA\Circles\Command
*/
class MembersDetails extends Base {
/** @var MemberRequest */
private $memberRequest;
/** @var FederatedUserService */
private $federatedUserService;
/** @var MemberService */
private $memberService;
/**
* MembersDetails constructor.
*
* @param MemberRequest $memberRequest
* @param FederatedUserService $federatedUserService
* @param MemberService $memberService
*/
public function __construct(
MemberRequest $memberRequest, FederatedUserService $federatedUserService, MemberService $memberService
) {
parent::__construct();
$this->memberRequest = $memberRequest;
$this->federatedUserService = $federatedUserService;
$this->memberService = $memberService;
}
protected function configure() {
parent::configure();
$this->setName('circles:members:details')
->setDescription('get details about a member by its ID')
->addArgument('member_id', InputArgument::REQUIRED, 'ID of the member')
->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
->addOption('initiator-type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0')
->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws CircleNotFoundException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InitiatorNotFoundException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
* @throws FederatedItemException
* @throws InvalidIdException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$memberId = $input->getArgument('member_id');
$circleId = $this->memberRequest->getMemberById($memberId)->getCircleId();
$this->federatedUserService->commandLineInitiator(
$input->getOption('initiator'),
Member::parseTypeString($input->getOption('initiator-type')),
$circleId,
false
);
$member = $this->memberService->getMemberById($memberId);
$output->writeln(json_encode($member, JSON_PRETTY_PRINT));
return 0;
}
}
@@ -0,0 +1,169 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\Db\MemberRequest;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedEventException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotConfirmedException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\ParseMemberLevelException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\MemberService;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class MembersLevel
*
* @package OCA\Circles\Command
*/
class MembersLevel extends Base {
/** @var MemberRequest */
private $memberRequest;
/** @var FederatedUserService */
private $federatedUserService;
/** @var MemberService */
private $memberService;
/**
* MembersLevel constructor.
*
* @param MemberRequest $memberRequest
* @param FederatedUserService $federatedUserService
* @param MemberService $memberService
*/
public function __construct(
MemberRequest $memberRequest, FederatedUserService $federatedUserService, MemberService $memberService
) {
parent::__construct();
$this->memberRequest = $memberRequest;
$this->federatedUserService = $federatedUserService;
$this->memberService = $memberService;
}
protected function configure() {
parent::configure();
$this->setName('circles:members:level')
->setDescription('Change the level of a member from a Circle')
->addArgument('member_id', InputArgument::REQUIRED, 'ID of the member from the Circle')
->addOption('circle', '', InputOption::VALUE_REQUIRED, 'ID of the circle', '')
->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
->addOption('initiator-type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0')
->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception')
->addArgument('level', InputArgument::REQUIRED, 'new level');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws FederatedEventException
* @throws FederatedItemException
* @throws InitiatorNotFoundException
* @throws RequestBuilderException
* @throws CircleNotFoundException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InitiatorNotConfirmedException
* @throws InvalidIdException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws ParseMemberLevelException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws SingleCircleNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$memberId = $input->getArgument('member_id');
$circleId = $input->getOption('circle');
try {
if ($circleId === '') {
$circleId = $this->memberRequest->getMemberById($memberId)->getCircleId();
}
$this->federatedUserService->commandLineInitiator(
$input->getOption('initiator'),
Member::parseTypeString($input->getOption('initiator-type')),
$circleId,
false
);
$this->memberService->getMemberById($memberId, $circleId);
$level = Member::parseLevelString($input->getArgument('level'));
$outcome = $this->memberService->memberLevel($memberId, $level);
} catch (FederatedItemException $e) {
if ($input->getOption('status-code')) {
throw new FederatedItemException(
' [' . get_class($e) . ', ' . $e->getStatus() . ']' . "\n" . $e->getMessage()
);
}
throw $e;
}
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($outcome, JSON_PRETTY_PRINT));
}
return 0;
}
}
@@ -0,0 +1,473 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use Exception;
use OC\Core\Command\Base;
use OCA\Circles\Db\MemberRequest;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\MemberService;
use OCA\Circles\Service\RemoteService;
use OCA\Circles\Tools\Exceptions\InvalidItemException;
use OCA\Circles\Tools\Exceptions\ItemNotFoundException;
use OCA\Circles\Tools\Exceptions\RequestNetworkException;
use OCA\Circles\Tools\Exceptions\SignatoryException;
use OCA\Circles\Tools\Exceptions\UnknownTypeException;
use OCA\Circles\Tools\Model\SimpleDataStore;
use OCA\Circles\Tools\Model\TreeNode;
use OCA\Circles\Tools\Traits\TConsoleTree;
use OCA\Circles\Tools\Traits\TStringTools;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class MembersList
*
* @package OCA\Circles\Command
*/
class MembersList extends Base {
use TConsoleTree;
use TStringTools;
/** @var MemberRequest */
private $memberRequest;
/** @var FederatedUserService */
private $federatedUserService;
/** @var RemoteService */
private $remoteService;
/** @var CircleService */
private $circleService;
/** @var MemberService */
private $memberService;
/** @var ConfigService */
private $configService;
/** @var InputInterface */
private $input;
/** @var string */
private $treeType = '';
/**
* MembersList constructor.
*
* @param MemberRequest $memberRequest
* @param FederatedUserService $federatedUserService
* @param RemoteService $remoteService
* @param CircleService $circleService
* @param MemberService $memberService
* @param ConfigService $configService
*/
public function __construct(
MemberRequest $memberRequest, FederatedUserService $federatedUserService,
RemoteService $remoteService, CircleService $circleService, MemberService $memberService,
ConfigService $configService
) {
parent::__construct();
$this->memberRequest = $memberRequest;
$this->federatedUserService = $federatedUserService;
$this->remoteService = $remoteService;
$this->circleService = $circleService;
$this->memberService = $memberService;
$this->configService = $configService;
}
protected function configure() {
parent::configure();
$this->setName('circles:members:list')
->setDescription('listing Members from a Circle')
->addArgument('circle_id', InputArgument::REQUIRED, 'ID of the circle')
->addOption('instance', '', InputOption::VALUE_REQUIRED, 'Instance of the circle', '')
->addOption('inherited', '', InputOption::VALUE_NONE, 'Display all inherited members')
->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
->addOption('initiator-type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0')
->addOption('display-name', '', InputOption::VALUE_NONE, 'display the displayName')
->addOption('tree', '', InputOption::VALUE_OPTIONAL, 'display members as a tree', false);
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws CircleNotFoundException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InitiatorNotFoundException
* @throws InvalidIdException
* @throws InvalidItemException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestNetworkException
* @throws SignatoryException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
* @throws FederatedItemException
* @throws SingleCircleNotFoundException
* @throws RequestBuilderException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$this->input = $input;
$circleId = $input->getArgument('circle_id');
$instance = $input->getOption('instance');
$initiator = $input->getOption('initiator');
$initiatorType = Member::parseTypeString($input->getOption('initiator-type'));
$inherited = $input->getOption('inherited');
$tree = null;
if ($input->getOption('tree') !== false) {
$this->treeType = (is_null($input->getOption('tree'))) ? 'all' : $input->getOption('tree');
$this->federatedUserService->commandLineInitiator($initiator, $initiatorType, $circleId, true);
$circle = $this->circleService->getCircle($circleId);
$output->writeln('<info>Name</info>: ' . $circle->getName());
$owner = $circle->getOwner();
$output->writeln('<info>Owner</info>: ' . $owner->getUserId() . '@' . $owner->getInstance());
$type = implode(", ", Circle::getCircleFlags($circle, Circle::FLAGS_LONG));
$output->writeln('<info>Config</info>: ' . $type);
$output->writeln(' ');
$tree = new TreeNode(null, new SimpleDataStore(['circle' => $circle]));
$inherited = false;
}
if ($inherited) {
$this->federatedUserService->commandLineInitiator($initiator, $initiatorType, $circleId, true);
$circle = $this->circleService->getCircle($circleId);
$members = $circle->getInheritedMembers(true);
} else {
$members = $this->getMembers($circleId, $instance, $initiator, $initiatorType, $tree);
}
if (!is_null($tree)) {
$this->drawTree(
$tree, [$this, 'displayLeaf'],
[
'height' => 3,
'node-spacing' => 1,
'item-spacing' => 0,
]
);
return 0;
}
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($members, JSON_PRETTY_PRINT));
return 0;
}
$output = new ConsoleOutput();
$output = $output->section();
$table = new Table($output);
$table->setHeaders(
[
'Circle Id', 'Circle Name', 'Member Id', 'Single Id', 'Type', 'Source',
'Username', 'Level', 'Invited By'
]
);
$table->render();
foreach ($members as $member) {
if ($member->getCircleId() === $circleId) {
$level = $member->getLevel();
} else {
$level = $member->getInheritanceFrom()->getLevel();
}
$table->appendRow(
[
$member->getCircleId(),
$member->getCircle()->getDisplayName(),
$member->getId(),
$member->getSingleId(),
Member::$TYPE[$member->getUserType()],
$member->hasBasedOn() ? Circle::$DEF_SOURCE[$member->getBasedOn()->getSource()] : '',
$this->configService->displayFederatedUser(
$member,
$this->input->getOption('display-name')
),
($level > 0) ? Member::$DEF_LEVEL[$level] :
'(' . strtolower($member->getStatus()) . ')',
($member->hasInvitedBy()) ? $this->configService->displayFederatedUser(
$member->getInvitedBy(),
$this->input->getOption('display-name')
) : 'Unknown'
]
);
}
return 0;
}
/**
* @param string $circleId
* @param string $instance
* @param string $initiator
* @param int $initiatorType
* @param TreeNode|null $tree
* @param array $knownIds
*
* @return array
* @throws CircleNotFoundException
* @throws FederatedItemException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InitiatorNotFoundException
* @throws InvalidIdException
* @throws InvalidItemException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws RequestNetworkException
* @throws SignatoryException
* @throws SingleCircleNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
*/
private function getMembers(
string $circleId,
string $instance,
string $initiator,
int $initiatorType,
?TreeNode $tree,
array $knownIds = []
): array {
if (in_array($circleId, $knownIds)) {
return [];
}
$knownIds[] = $circleId;
if (!$this->configService->isLocalInstance($instance)) {
$data = [];
if ($initiator) {
$data['initiator'] = $this->federatedUserService->getFederatedUser(
$initiator,
Member::TYPE_USER
);
}
try {
$members = $this->remoteService->getMembersFromInstance($circleId, $instance, $data);
} catch (RemoteInstanceException $e) {
return [];
}
} else {
$this->federatedUserService->commandLineInitiator($initiator, $initiatorType, $circleId, true);
$members = $this->memberService->getMembers($circleId);
}
if (!is_null($tree)) {
foreach ($members as $member) {
if ($member->getUserType() === Member::TYPE_CIRCLE) {
if (!$this->configService->isLocalInstance($member->getInstance())) {
$data = [];
if ($initiator) {
$data['initiator'] = $this->federatedUserService->getFederatedUser(
$initiator,
Member::TYPE_USER
);
}
$circle = null;
try {
$circle = $this->remoteService->getCircleFromInstance(
$member->getSingleId(), $member->getInstance(), $data
);
} catch (CircleNotFoundException | RemoteInstanceException $e) {
}
} else {
$this->federatedUserService->commandLineInitiator(
$initiator,
$initiatorType,
$member->getSingleId(),
true
);
$circle = $this->circleService->getCircle($member->getSingleId());
}
$node = new TreeNode(
$tree, new SimpleDataStore(
[
'circle' => $circle,
'member' => $member,
'cycling' => in_array($member->getSingleId(), $knownIds),
]
)
);
$this->getMembers(
$member->getSingleId(),
$member->getInstance(),
$initiator,
$initiatorType,
$node,
$knownIds
);
} else {
if ($this->treeType !== 'circles-only') {
new TreeNode(
$tree, new SimpleDataStore(
[
'member' => $member,
'cycling' => in_array($member->getSingleId(), $knownIds)
]
)
);
}
}
}
}
return $members;
}
/**
* @param SimpleDataStore $data
* @param int $lineNumber
*
* @return string
* @throws OwnerNotFoundException
*/
public function displayLeaf(SimpleDataStore $data, int $lineNumber): string {
if ($lineNumber === 3) {
return ($data->gBool('cycling')) ? '<comment>(loop detected)</comment>' : '';
}
try {
$line = '';
$circle = null;
if ($data->hasKey('circle')) {
/** @var Circle $circle */
try {
$circle = $data->gObj('circle', Circle::class);
} catch (Exception $e) {
}
}
if ($data->hasKey('member')) {
/** @var Member $member */
$member = $data->gObj('member', Member::class);
if ($lineNumber === 1) {
$line .= '<info>' . $member->getSingleId() . '</info>';
if (!$this->configService->isLocalInstance($member->getInstance())) {
$line .= '@' . $member->getInstance();
}
$line .= ' (' . Member::$DEF_LEVEL[$member->getLevel()] . ')';
$line .= ' <info>MemberId</info>: ' . $member->getId();
$line .= ' <info>Name</info>: ' . $this->configService->displayFederatedUser(
$member,
$this->input->getOption('display-name')
);
if ($member->hasBasedOn()) {
$line .= ' <info>Source</info>: '
. Circle::$DEF_SOURCE[$member->getBasedOn()->getSource()];
} else {
$line .= ' <info>Type</info>: ' . Member::$TYPE[$member->getUserType()];
}
}
if ($lineNumber === 2) {
if (is_null($circle)) {
if ($member->getUserType() === Member::TYPE_CIRCLE) {
$line .= '<comment>(out of bounds)</comment>';
}
return $line;
}
$owner = $circle->getOwner();
$line .= '<info>Owner</info>: ' . $owner->getUserId() . '@' . $owner->getInstance();
$type = implode(", ", Circle::getCircleFlags($circle, Circle::FLAGS_LONG));
$line .= ($type === '') ? '' : ' <info>Config</info>: ' . $type;
}
} else {
if ($lineNumber === 1 && !is_null($circle)) {
$line .= '<info>' . $circle->getSingleId() . '</info>';
if (!$this->configService->isLocalInstance($circle->getInstance())) {
$line .= '@' . $circle->getInstance();
}
}
}
return $line;
} catch (InvalidItemException | ItemNotFoundException | UnknownTypeException $e) {
}
return '';
}
}
@@ -0,0 +1,147 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\Db\MemberRequest;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedEventException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotConfirmedException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\MemberService;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class MembersRemove
*
* @package OCA\Circles\Command
*/
class MembersRemove extends Base {
/** @var MemberRequest */
private $memberRequest;
/** @var FederatedUserService */
private $federatedUserService;
/** @var MemberService */
private $memberService;
/**
* MembersRemove constructor.
*
* @param MemberRequest $memberRequest
* @param FederatedUserService $federatedUserService
* @param MemberService $memberService
*/
public function __construct(
MemberRequest $memberRequest, FederatedUserService $federatedUserService, MemberService $memberService
) {
parent::__construct();
$this->memberRequest = $memberRequest;
$this->federatedUserService = $federatedUserService;
$this->memberService = $memberService;
}
protected function configure() {
parent::configure();
$this->setName('circles:members:remove')
->setDescription('remove a member from a circle')
->addArgument('member_id', InputArgument::REQUIRED, 'ID of the member from the Circle')
->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
->addOption('initiator-type', '', InputOption::VALUE_REQUIRED, 'set initiator type', '0');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws CircleNotFoundException
* @throws FederatedEventException
* @throws FederatedItemException
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InitiatorNotConfirmedException
* @throws InitiatorNotFoundException
* @throws InvalidIdException
* @throws MemberNotFoundException
* @throws OwnerNotFoundException
* @throws RemoteInstanceException
* @throws RemoteNotFoundException
* @throws RemoteResourceNotFoundException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
* @throws UnknownRemoteException
* @throws UserTypeNotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$memberId = $input->getArgument('member_id');
$member = $this->memberRequest->getMemberById($memberId);
$this->federatedUserService->commandLineInitiator(
$input->getOption('initiator'),
Member::parseTypeString($input->getOption('initiator-type')),
$member->getCircleId(),
false
);
$outcome = $this->memberService->removeMember($memberId);
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($outcome, JSON_PRETTY_PRINT));
}
return 0;
}
}
@@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OC\Core\Command\Base;
use OCA\Circles\IFederatedUser;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\SearchResult;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\SearchService;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class MembersSearch
*
* @package OCA\Circles\Command
*/
class MembersSearch extends Base {
/** @var SearchService */
private $searchService;
/** @var ConfigService */
private $configService;
/**
* MembersSearch constructor.
*
* @param SearchService $searchService
* @param ConfigService $configService
*/
public function __construct(SearchService $searchService, ConfigService $configService) {
parent::__construct();
$this->searchService = $searchService;
$this->configService = $configService;
}
protected function configure() {
parent::configure();
$this->setName('circles:members:search')
->setDescription('Change the level of a member from a Circle')
->addArgument('term', InputArgument::REQUIRED, 'term to search')
->addOption('initiator', '', InputOption::VALUE_REQUIRED, 'set an initiator to the request', '')
->addOption('status-code', '', InputOption::VALUE_NONE, 'display status code on exception');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$result = $this->searchService->search($input->getArgument('needle'));
if (strtolower($input->getOption('output')) === 'json') {
$output->writeln(json_encode($result, JSON_PRETTY_PRINT));
}
$this->displaySearchResult($result);
return 0;
}
/**
* @param list<IFederatedUser|SearchResult> $result
*/
private function displaySearchResult(array $result) {
$output = new ConsoleOutput();
$output = $output->section();
$table = new Table($output);
$table->setHeaders(['SingleId', 'UserId', 'UserType', 'Instance']);
$table->render();
foreach ($result as $entry) {
if (!$result instanceof IFederatedUser) {
continue;
}
$table->appendRow(
[
$entry->getSingleId(),
$entry->getUserId(),
Member::$TYPE[$entry->getUserType()],
$this->configService->displayInstance($entry->getInstance())
]
);
}
}
}
@@ -0,0 +1,320 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use OCA\Circles\Tools\Traits\TArrayTools;
use OC\Core\Command\Base;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Model\ShareWrapper;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\ShareWrapperService;
use Symfony\Component\Console\Exception\MissingInputException;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class SharesFilesList
*
* @package OCA\Circles\Command
*/
class SharesFiles extends Base {
use TArrayTools;
/** @var FederatedUserService */
private $federatedUserService;
/** @var ShareWrapperService */
private $shareWrapperService;
/** @var ConfigService */
private $configService;
/** @var int */
private $fileId = 0;
/**
* SharesFilesList constructor.
*
* @param FederatedUserService $federatedUserService
* @param ShareWrapperService $shareWrapperService
* @param ConfigService $configService
*/
public function __construct(
FederatedUserService $federatedUserService, ShareWrapperService $shareWrapperService,
ConfigService $configService
) {
parent::__construct();
$this->federatedUserService = $federatedUserService;
$this->shareWrapperService = $shareWrapperService;
$this->configService = $configService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:shares:files')
->setDescription('listing shares files')
->addArgument('file_id', InputArgument::OPTIONAL, 'filter on a File Id', '0')
->addOption('to', '', InputOption::VALUE_REQUIRED, 'get files shared TO CIRCLEID', '')
->addOption('with', '', InputOption::VALUE_REQUIRED, 'get files shared WITH USERID', '')
->addOption('by', '', InputOption::VALUE_REQUIRED, 'get files shared BY USERID', '')
->addOption('all', '', InputOption::VALUE_NONE, 'get all data about the shares');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InvalidIdException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$this->fileId = (int)$input->getArgument('file_id');
$json = (strtolower($input->getOption('output')) === 'json');
$this->displayShares(
(int)$input->getArgument('file_id'),
$input->getOption('to'),
$input->getOption('with'),
$input->getOption('by'),
$input->getOption('all'),
$json
);
return 0;
//
// if ($input->getOption('to')) {
// $this->sharedToCircle(
// $input->getOption('to'),
// $input->getOption('with'),
// $input->getOption('by'),
// $input->getOption('all'),
// $json
// );
//
// return 0;
// }
//
// if ($input->getOption('with')) {
// $this->sharedWith($input->getOption('with'), $json);
//
// return 0;
// }
//
// if ($input->getOption('by')) {
// $this->sharesBy($input->getOption('by'), $json);
//
// return 0;
// }
//
// if ($this->fileId > 0) {
// $this->sharedFile($json);
//
// return 0;
// }
}
/**
* @param int $fileId
* @param string $to
* @param string $with
* @param string $by
* @param bool $all
* @param bool $json
*
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InvalidIdException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
*/
private function displayShares(
int $fileId,
string $to,
string $with,
string $by,
bool $all,
bool $json
): void {
$shareWrappers = $this->getShares($fileId, $to, $with, $by, $all, $filterRecipient);
$output = new ConsoleOutput();
if ($json) {
$output->writeln(json_encode($shareWrappers, JSON_PRETTY_PRINT));
return;
}
$output = $output->section();
$table = new Table($output);
$headers = [
'Share Id',
'File Id',
'File Owner',
'Original Filename',
'Shared By',
'Shared To'
];
if (!$filterRecipient) {
$headers = array_merge($headers, ['Recipient', 'Target Name']);
}
$table->setHeaders($headers);
$table->render();
foreach ($shareWrappers as $share) {
if (!$filterRecipient) {
$recipient = $share->getInitiator();
$sharedTo = $recipient->getDisplayName();
if (!$this->configService->isLocalInstance($recipient->getInstance())) {
$sharedTo .= '@' . $recipient->getInstance();
}
}
$circle = $share->getCircle();
$row = [
$share->getId(),
$share->getFileSource(),
$share->getShareOwner(),
$share->getFileTarget(),
$share->getSharedBy(),
$circle->getDisplayName() . ' (' . $share->getSharedWith()
. ', ' . Circle::$DEF_SOURCE[$circle->getSource()] . ')'
];
if (!$filterRecipient) {
$row = array_merge(
$row, [
$sharedTo . ' (' . $recipient->getSingleId() . ', '
. Circle::$DEF_SOURCE[$recipient->getBasedOn()->getSource()] . ')',
(($share->getChildId() > 0) ? $share->getChildFileTarget(
) : $share->getFileTarget()),
]
);
}
$table->appendRow($row);
}
}
/**
* @param int $fileId
* @param string $to
* @param string $with
* @param string $by
* @param bool $all
* @param bool|null $filterRecipient
*
* @return ShareWrapper[]
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InvalidIdException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
*/
private function getShares(
int $fileId,
string $to,
string $with,
string $by,
bool $all,
?bool &$filterRecipient = false
): array {
if ($fileId > 0) {
return $this->shareWrapperService->getSharesByFileId($this->fileId, true);
}
if ($to !== '') {
$filterRecipient = !$all;
return $this->shareWrapperService->getSharesToCircle(
$to,
($with === '') ? null : $this->federatedUserService->getLocalFederatedUser($with),
($by === '') ? null : $this->federatedUserService->getLocalFederatedUser($by),
$all
);
}
if ($by !== '') {
$filterRecipient = !$all;
return $this->shareWrapperService->getSharesBy(
$this->federatedUserService->getLocalFederatedUser($by),
$fileId,
true,
-1,
0,
true,
$all
);
}
if ($with !== '') {
$probe = new CircleProbe();
$probe->includePersonalCircles();
return $this->shareWrapperService->getSharedWith(
$this->federatedUserService->getLocalFederatedUser($with),
$fileId,
$probe
);
}
throw new MissingInputException(
'Specify a FileId or an option: --with (USER), --by (USER), --to (CIRCLE)'
);
}
}
@@ -0,0 +1,161 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use Exception;
use OC\Core\Command\Base;
use OCA\Circles\Service\DavService;
use OCA\DAV\CardDAV\CardDavBackend;
use OCP\IUserManager;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class SyncContact
*
* @package OCA\Circles\Command
*/
class SyncContact extends Base {
/** @var IUserManager */
private $userManager;
/** @var CardDavBackend */
private $cardDavBackend;
/** @var DavService */
private $davService;
/**
* Groups constructor.
*
* @param IUserManager $userManager
* @param CardDavBackend $cardDavBackend
* @param DavService $davService
*/
public function __construct(
IUserManager $userManager, CardDavBackend $cardDavBackend, DavService $davService
) {
parent::__construct();
$this->userManager = $userManager;
$this->cardDavBackend = $cardDavBackend;
$this->davService = $davService;
}
protected function configure() {
parent::configure();
$this->setName('circles:contacts:sync')
->addOption('info', '', InputOption::VALUE_NONE, 'get info about contacts')
->addOption('status', '', InputOption::VALUE_NONE, 'get info about sync')
->setDescription('sync contacts, when using the Circles app as a backend of the Contact app');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int|void|null
* @throws Exception
*/
protected function execute(InputInterface $input, OutputInterface $output) {
if ($input->getOption('info')) {
$this->displayInfo($output);
return;
}
if ($input->getOption('status')) {
$this->displayStatus($output);
return;
}
$this->davService->migration();
$output->writeln('migration done');
}
/**
* @param OutputInterface $output
*/
private function displayInfo(OutputInterface $output) {
$users = $this->userManager->search('');
$tCards = $tBooks = 0;
$knownBooks = [];
foreach ($users as $user) {
$books = $this->cardDavBackend->getAddressBooksForUser('principals/users/' . $user->getUID());
$output->writeln(
'- User <info>' . $user->getUID() . '</info> have ' . sizeof($books) . ' address books:'
);
$tBooks += sizeof($books);
foreach ($books as $book) {
$bookId = $book['id'];
$owner = $this->davService->getOwnerFromAddressBook($bookId);
$cards = $this->cardDavBackend->getCards($bookId);
if (!in_array($bookId, $knownBooks)) {
$tCards += sizeof($cards);
}
$shared = '';
if ($owner !== $user->getUID()) {
$shared = ' (shared by <info>' . $owner . '</info>)';
}
$output->writeln(
' <comment>*</comment> book #' . $bookId . $shared . ' contains '
. sizeof($cards)
. ' entries'
);
$knownBooks[] = $bookId;
}
}
$output->writeln('');
$output->writeln('with a total of ' . $tBooks . ' address books and ' . $tCards . ' contact entries');
}
/**
* @param OutputInterface $output
*/
private function displayStatus(OutputInterface $output) {
$output->writeln('not yet available');
}
}
@@ -0,0 +1,548 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Controller;
use OCA\Circles\Tools\Traits\TDeserialize;
use OCA\Circles\Tools\Traits\TNCLogger;
use Exception;
use OCA\Circles\Exceptions\ContactAddressBookNotFoundException;
use OCA\Circles\Exceptions\ContactFormatException;
use OCA\Circles\Exceptions\ContactNotFoundException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Model\FederatedUser;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\Probes\BasicProbe;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\MemberService;
use OCA\Circles\Service\MembershipService;
use OCA\Circles\Service\SearchService;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
use OCP\IUserSession;
/**
* Class AdminController
*
* @package OCA\Circles\Controller
*/
class AdminController extends OCSController {
use TDeserialize;
use TNCLogger;
/** @var IUserSession */
private $userSession;
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/** @var MemberService */
private $memberService;
/** @var MembershipService */
private $membershipService;
/** @var SearchService */
private $searchService;
/** @var ConfigService */
protected $configService;
/**
* LocalController constructor.
*
* @param string $appName
* @param IRequest $request
* @param IUserSession $userSession
* @param FederatedUserService $federatedUserService
* @param CircleService $circleService
* @param MemberService $memberService
* @param MembershipService $membershipService
* @param SearchService $searchService
* @param ConfigService $configService
*/
public function __construct(
string $appName,
IRequest $request,
IUserSession $userSession,
FederatedUserService $federatedUserService,
CircleService $circleService,
MemberService $memberService,
MembershipService $membershipService,
SearchService $searchService,
ConfigService $configService
) {
parent::__construct($appName, $request);
$this->userSession = $userSession;
$this->federatedUserService = $federatedUserService;
$this->circleService = $circleService;
$this->memberService = $memberService;
$this->membershipService = $membershipService;
$this->searchService = $searchService;
$this->configService = $configService;
$this->setup('app', 'circles');
}
/**
* @param string $emulated
* @param string $name
* @param bool $personal
* @param bool $local
*
* @return DataResponse
* @throws OCSException
*/
public function create(
string $emulated,
string $name,
bool $personal = false,
bool $local = false
): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$circle = $this->circleService->create($name, null, $personal, $local);
return new DataResponse($this->serializeArray($circle));
} catch (Exception $e) {
$this->e(
$e, [
'emulated' => $emulated,
'name' => $name,
'members' => $personal,
'local' => $local
]
);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
*
* @return DataResponse
* @throws OCSException
*/
public function destroy(string $emulated, string $circleId): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$circle = $this->circleService->destroy($circleId);
return new DataResponse($this->serializeArray($circle));
} catch (Exception $e) {
$this->e($e, ['emulated' => $emulated, 'circleId' => $circleId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
* @param string $userId
* @param int $type
*
* @return DataResponse
* @throws OCSException
*/
public function memberAdd(string $emulated, string $circleId, string $userId, int $type): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
// exception in Contact
if ($type === Member::TYPE_CONTACT) {
$currentUser = $this->federatedUserService->getCurrentUser();
if (!$this->configService->isLocalInstance($currentUser->getInstance())) {
throw new OCSException('works only from local instance', 404);
}
$userId = $currentUser->getUserId() . '/' . $userId;
}
$federatedUser = $this->federatedUserService->generateFederatedUser($userId, $type);
$result = $this->memberService->addMember($circleId, $federatedUser);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e(
$e, [
'emulated' => $emulated,
'circleId' => $circleId,
'userId' => $userId,
'type' => $type
]
);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
* @param string $memberId
* @param string|int $level
*
* @return DataResponse
* @throws OCSException
*/
public function memberLevel(string $emulated, string $circleId, string $memberId, $level): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
if (is_int($level)) {
$level = Member::parseLevelInt($level);
} else {
$level = Member::parseLevelString($level);
}
$this->memberService->getMemberById($memberId, $circleId);
$result = $this->memberService->memberLevel($memberId, $level);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e(
$e,
[
'emulated' => $emulated,
'circleId' => $circleId,
'memberId' => $memberId,
'level' => $level
]
);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param int $limit
* @param int $offset
* @return DataResponse
* @throws OCSException
*/
public function circles(string $emulated, int $limit = -1, int $offset = 0): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$probe = new CircleProbe();
$probe->filterHiddenCircles()
->filterBackendCircles()
->addDetail(BasicProbe::DETAILS_POPULATION)
->setItemsLimit($limit)
->setItemsOffset($offset);
return new DataResponse($this->serializeArray($this->circleService->getCircles($probe)));
} catch (Exception $e) {
$this->e($e, ['emulated' => $emulated]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
*
* @return DataResponse
* @throws OCSException
*/
public function circleDetails(string $emulated, string $circleId): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$probe = new CircleProbe();
$probe->includeNonVisibleCircles();
return new DataResponse($this->serialize($this->circleService->getCircle($circleId, $probe)));
} catch (Exception $e) {
$this->e($e, ['emulated' => $emulated, 'circleId' => $circleId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
*
* @return DataResponse
* @throws OCSException
*/
public function circleJoin(string $emulated, string $circleId): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$result = $this->circleService->circleJoin($circleId);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e($e, ['emulated' => $emulated, 'circleId' => $circleId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
*
* @return DataResponse
* @throws OCSException
*/
public function circleLeave(string $emulated, string $circleId): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$result = $this->circleService->circleLeave($circleId);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e($e, ['emulated' => $emulated, 'circleId' => $circleId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
* @param string $memberId
*
* @return DataResponse
* @throws OCSException
*/
public function memberConfirm(string $emulated, string $circleId, string $memberId): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$member = $this->memberService->getMemberById($memberId, $circleId);
$federatedUser = new FederatedUser();
$federatedUser->importFromIFederatedUser($member);
$result = $this->memberService->addMember($circleId, $federatedUser);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e($e, ['emulated' => $emulated, 'circleId' => $circleId, 'memberId' => $memberId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
* @param string $memberId
*
* @return DataResponse
* @throws OCSException
*/
public function memberRemove(string $emulated, string $circleId, string $memberId): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$this->memberService->getMemberById($memberId, $circleId);
$result = $this->memberService->removeMember($memberId);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e($e, ['emulated' => $emulated, 'circleId' => $circleId, 'memberId' => $memberId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
*
* @return DataResponse
* @throws OCSException
*/
public function members(string $emulated, string $circleId): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
return new DataResponse($this->serializeArray($this->memberService->getMembers($circleId)));
} catch (Exception $e) {
$this->e($e, ['emulated' => $emulated, 'circleId' => $circleId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
* @param string $value
*
* @return DataResponse
* @throws OCSException
*/
public function editName(string $emulated, string $circleId, string $value): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$outcome = $this->circleService->updateName($circleId, $value);
return new DataResponse($this->serializeArray($outcome));
} catch (Exception $e) {
$this->e($e, ['emulated' => $emulated, 'circleId' => $circleId, 'value' => $value]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
* @param string $value
*
* @return DataResponse
* @throws OCSException
*/
public function editDescription(string $emulated, string $circleId, string $value): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$outcome = $this->circleService->updateDescription($circleId, $value);
return new DataResponse($this->serializeArray($outcome));
} catch (Exception $e) {
$this->e($e, ['emulated' => $emulated, 'circleId' => $circleId, 'value' => $value]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
* @param string $setting
* @param string|null $value
*
* @return DataResponse
* @throws OCSException
*/
public function editSetting(string $emulated, string $circleId, string $setting, ?string $value = null): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$outcome = $this->circleService->updateSetting($circleId, $setting, $value);
return new DataResponse($this->serializeArray($outcome));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId, 'setting' => $setting, 'value' => $value]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
* @param int $value
*
* @return DataResponse
* @throws OCSException
*/
public function editConfig(string $emulated, string $circleId, int $value): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$outcome = $this->circleService->updateConfig($circleId, $value);
return new DataResponse($this->serializeArray($outcome));
} catch (Exception $e) {
$this->e($e, ['emulated' => $emulated, 'circleId' => $circleId, 'value' => $value]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
* @param string $circleId
* @param string $singleId
*
* @return DataResponse
* @throws OCSException
*/
public function link(string $emulated, string $circleId, string $singleId): DataResponse {
try {
$this->setLocalFederatedUser($emulated);
$membership = $this->membershipService->getMembership($circleId, $singleId, true);
return new DataResponse($this->serialize($membership));
} catch (Exception $e) {
$this->e($e, ['emulated' => $emulated, 'circleId' => $circleId, 'singleId' => $singleId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @param string $emulated
*
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws InvalidIdException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
* @throws ContactAddressBookNotFoundException
* @throws ContactFormatException
* @throws ContactNotFoundException
*/
private function setLocalFederatedUser(string $emulated): void {
$user = $this->userSession->getUser();
$this->federatedUserService->setCurrentPatron($user->getUID());
$this->federatedUserService->setLocalCurrentUserId($emulated);
}
}
@@ -0,0 +1,87 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@pontapreta.net>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Controller;
use Exception;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\FederatedUserService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
/**
* @deprecated
* re-implemented only to re-enable an old feature until we switch to a better integration.
*/
class DeprecatedController extends Controller {
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
public function __construct(
string $appName,
IRequest $request,
FederatedUserService $federatedUserService,
CircleService $circleService
) {
parent::__construct($appName, $request);
$this->federatedUserService = $federatedUserService;
$this->circleService = $circleService;
}
/**
* @NoAdminRequired
* @NoSubAdminRequired
*
* @param string $term
*
* @return DataResponse
*/
public function listing(string $term = ''): DataResponse {
try {
$this->federatedUserService->initCurrentUser();
$probe = new CircleProbe();
$filterCircle = new Circle();
$filterCircle->setName($term)
->setDisplayName($term);
$probe->setFilterCircle($filterCircle);
$probe->filterSystemCircles();
$data = $this->circleService->getCircles($probe);
return new DataResponse(['data' => $data]);
} catch (Exception $e) {
return new DataResponse([]);
}
}
}
@@ -0,0 +1,164 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Controller;
use OCA\Circles\AppInfo\Application;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\EventWrapperService;
use OCA\Circles\Service\FederatedEventService;
use OCA\Circles\Service\RemoteDownstreamService;
use OCA\Circles\Service\RemoteUpstreamService;
use OCA\Circles\Tools\Traits\TAsync;
use OCA\Circles\Tools\Traits\TStringTools;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
/**
* Class EventWrapperController
*
* @package OCA\Circles\Controller
*/
class EventWrapperController extends Controller {
use TStringTools;
use TAsync;
/** @var EventWrapperService */
private $eventWrapperService;
/** @var FederatedEventService */
private $federatedEventService;
/** @var RemoteUpstreamService */
private $remoteUpstreamService;
/** @var RemoteDownstreamService */
private $remoteDownstreamService;
/** @var ConfigService */
private $configService;
/**
* EventWrapperController constructor.
*
* @param string $appName
* @param IRequest $request
* @param EventWrapperService $eventWrapperService
* @param FederatedEventService $federatedEventService
* @param RemoteUpstreamService $remoteUpstreamService
* @param RemoteDownstreamService $remoteDownstreamService
* @param ConfigService $configService
*/
public function __construct(
string $appName,
IRequest $request,
EventWrapperService $eventWrapperService,
FederatedEventService $federatedEventService,
RemoteUpstreamService $remoteUpstreamService,
RemoteDownstreamService $remoteDownstreamService,
ConfigService $configService
) {
parent::__construct($appName, $request);
$this->eventWrapperService = $eventWrapperService;
$this->federatedEventService = $federatedEventService;
$this->remoteUpstreamService = $remoteUpstreamService;
$this->remoteDownstreamService = $remoteDownstreamService;
$this->configService = $configService;
$this->setup('app', Application::APP_ID);
$this->setupInt(self::$SETUP_TIME_LIMIT, 900);
}
/**
* Called locally.
*
* Async process and broadcast the event to every instances of GS
* This should be initiated by the instance that owns the Circles.
*
* @PublicPage
* @NoCSRFRequired
*
* @param string $token
*
* @return DataResponse
*/
public function asyncBroadcast(string $token): DataResponse {
$wrappers = $this->remoteUpstreamService->getEventsByToken($token);
if (empty($wrappers) && $token !== 'test-dummy-token') {
return new DataResponse([], Http::STATUS_OK);
}
// closing socket, keep current process running.
$this->async();
foreach ($wrappers as $wrapper) {
$this->eventWrapperService->manageWrapper($wrapper);
}
$this->eventWrapperService->confirmStatus($token);
// so circles:check can check async is fine
if ($token === 'test-dummy-token') {
sleep(4);
}
// exit() or useless log will be generated
exit();
}
// /**
// * Status Event. This is an event to check status of items between instances.
// *
// * @PublicPage
// * @NoCSRFRequired
// *
// * @return DataResponse
// */
// public function status(): DataResponse {
// $data = file_get_contents('php://input');
//
// try {
// $event = new GSEvent();
// $event->importFromJson($data);
// $this->gsDownstreamService->statusEvent($event);
//
// return $this->success(['success' => $event]);
// } catch (Exception $e) {
// return $this->fail(['data' => $data, 'error' => $e->getMessage()]);
// }
// }
}
@@ -0,0 +1,598 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Controller;
use Exception;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\FrontendException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Model\FederatedUser;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\Probes\BasicProbe;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\MemberService;
use OCA\Circles\Service\MembershipService;
use OCA\Circles\Service\PermissionService;
use OCA\Circles\Service\SearchService;
use OCA\Circles\Tools\Traits\TDeserialize;
use OCA\Circles\Tools\Traits\TNCLogger;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
use OCP\IUserSession;
/**
* Class LocalController
*
* @package OCA\Circles\Controller
*/
class LocalController extends OCSController {
use TDeserialize;
use TNCLogger;
/** @var IUserSession */
private $userSession;
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/** @var MemberService */
private $memberService;
/** @var MembershipService */
private $membershipService;
/** @var PermissionService */
private $permissionService;
/** @var SearchService */
private $searchService;
/** @var ConfigService */
protected $configService;
/**
* LocalController constructor.
*
* @param string $appName
* @param IRequest $request
* @param IUserSession $userSession
* @param FederatedUserService $federatedUserService
* @param CircleService $circleService
* @param MemberService $memberService
* @param MembershipService $membershipService
* @param SearchService $searchService
* @param ConfigService $configService
*/
public function __construct(
string $appName,
IRequest $request,
IUserSession $userSession,
FederatedUserService $federatedUserService,
CircleService $circleService,
MemberService $memberService,
MembershipService $membershipService,
PermissionService $permissionService,
SearchService $searchService,
ConfigService $configService
) {
parent::__construct($appName, $request);
$this->userSession = $userSession;
$this->federatedUserService = $federatedUserService;
$this->circleService = $circleService;
$this->memberService = $memberService;
$this->membershipService = $membershipService;
$this->permissionService = $permissionService;
$this->searchService = $searchService;
$this->configService = $configService;
$this->setup('app', 'circles');
}
/**
* @NoAdminRequired
*
* @param string $name
* @param bool $personal
* @param bool $local
*
* @return DataResponse
* @throws OCSException
*/
public function create(string $name, bool $personal = false, bool $local = false): DataResponse {
try {
$this->setCurrentFederatedUser();
$this->permissionService->confirmCircleCreation();
$circle = $this->circleService->create($name, null, $personal, $local);
return new DataResponse($this->serializeArray($circle));
} catch (Exception $e) {
$this->e($e, ['name' => $name, 'members' => $personal, 'local' => $local]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
*
* @return DataResponse
* @throws OCSException
*/
public function destroy(string $circleId): DataResponse {
try {
$this->setCurrentFederatedUser();
$circle = $this->circleService->destroy($circleId);
return new DataResponse($this->serializeArray($circle));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $term
*
* @return DataResponse
* @throws OCSException
*/
public function search(string $term): DataResponse {
try {
$this->setCurrentFederatedUser();
return new DataResponse($this->serializeArray($this->searchService->search($term)));
} catch (Exception $e) {
$this->e($e, ['term' => $term]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
*
* @return DataResponse
* @throws OCSException
*/
public function circleDetails(string $circleId): DataResponse {
try {
$this->setCurrentFederatedUser();
$probe = new CircleProbe();
$probe->includeNonVisibleCircles();
return new DataResponse($this->serialize($this->circleService->getCircle($circleId, $probe)));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
* @param string $userId
* @param int $type
*
* @return DataResponse
* @throws OCSException
*/
public function memberAdd(string $circleId, string $userId, int $type): DataResponse {
try {
$this->setCurrentFederatedUser();
// exception in Contact
if ($type === Member::TYPE_CONTACT) {
$currentUser = $this->federatedUserService->getCurrentUser();
if (!$this->configService->isLocalInstance($currentUser->getInstance())) {
throw new OCSException('works only from local instance', 404);
}
$userId = $currentUser->getUserId() . '/' . $userId;
}
$federatedUser = $this->federatedUserService->generateFederatedUser($userId, $type);
$result = $this->memberService->addMember($circleId, $federatedUser);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId, 'userId' => $userId, 'type' => $type]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
* @param array $members
*
* @return DataResponse
* @throws OCSException
*/
public function membersAdd(string $circleId, array $members): DataResponse {
try {
$this->setCurrentFederatedUser();
$federatedUsers = [];
foreach ($members as $member) {
// TODO: generate Multiple FederatedUsers using a single SQL request
$federatedUsers[] = $this->federatedUserService->generateFederatedUser(
$this->get('id', $member),
$this->getInt('type', $member)
);
}
$result = $this->memberService->addMembers($circleId, $federatedUsers);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId, 'members' => $members]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
*
* @return DataResponse
* @throws OCSException
*/
public function circleJoin(string $circleId): DataResponse {
try {
$this->setCurrentFederatedUser();
$result = $this->circleService->circleJoin($circleId);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
*
* @return DataResponse
* @throws OCSException
*/
public function circleLeave(string $circleId): DataResponse {
try {
$this->setCurrentFederatedUser();
$result = $this->circleService->circleLeave($circleId);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
* @param string $memberId
* @param string|int $level
*
* @return DataResponse
* @throws OCSException
*/
public function memberLevel(string $circleId, string $memberId, $level): DataResponse {
try {
$this->setCurrentFederatedUser();
if (is_int($level)) {
$level = Member::parseLevelInt($level);
} else {
$level = Member::parseLevelString($level);
}
$this->memberService->getMemberById($memberId, $circleId);
$result = $this->memberService->memberLevel($memberId, $level);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId, 'memberId' => $memberId, 'level' => $level]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
* @param string $memberId
*
* @return DataResponse
* @throws OCSException
*/
public function memberConfirm(string $circleId, string $memberId): DataResponse {
try {
$this->setCurrentFederatedUser();
$member = $this->memberService->getMemberById($memberId, $circleId);
$federatedUser = new FederatedUser();
$federatedUser->importFromIFederatedUser($member);
$result = $this->memberService->addMember($circleId, $federatedUser);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId, 'memberId' => $memberId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
* @param string $memberId
*
* @return DataResponse
* @throws OCSException
*/
public function memberRemove(string $circleId, string $memberId): DataResponse {
try {
$this->setCurrentFederatedUser();
$this->memberService->getMemberById($memberId, $circleId);
$result = $this->memberService->removeMember($memberId);
return new DataResponse($this->serializeArray($result));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId, 'memberId' => $memberId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param int $limit
* @param int $offset
*
* @return DataResponse
* @throws OCSException
*/
public function circles(int $limit = -1, int $offset = 0): DataResponse {
try {
$this->setCurrentFederatedUser();
$probe = new CircleProbe();
$probe->filterHiddenCircles()
->filterBackendCircles()
->addDetail(BasicProbe::DETAILS_POPULATION)
->setItemsLimit($limit)
->setItemsOffset($offset);
return new DataResponse($this->serializeArray($this->circleService->getCircles($probe)));
} catch (Exception $e) {
$this->e($e);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
*
* @return DataResponse
* @throws OCSException
*/
public function members(string $circleId): DataResponse {
try {
$this->setCurrentFederatedUser();
return new DataResponse($this->serializeArray($this->memberService->getMembers($circleId)));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
* @param string $value
*
* @return DataResponse
* @throws OCSException
*/
public function editName(string $circleId, string $value): DataResponse {
try {
$this->setCurrentFederatedUser();
$outcome = $this->circleService->updateName($circleId, $value);
return new DataResponse($this->serializeArray($outcome));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId, 'value' => $value]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
* @param string $value
*
* @return DataResponse
* @throws OCSException
*/
public function editDescription(string $circleId, string $value): DataResponse {
try {
$this->setCurrentFederatedUser();
$outcome = $this->circleService->updateDescription($circleId, $value);
return new DataResponse($this->serializeArray($outcome));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId, 'value' => $value]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
* @param string $setting
* @param string|null $value
*
* @return DataResponse
* @throws OCSException
*/
public function editSetting(string $circleId, string $setting, ?string $value = null): DataResponse {
try {
$this->setCurrentFederatedUser();
$outcome = $this->circleService->updateSetting($circleId, $setting, $value);
return new DataResponse($this->serializeArray($outcome));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId, 'setting' => $setting, 'value' => $value]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
* @param int $value
*
* @return DataResponse
* @throws OCSException
*/
public function editConfig(string $circleId, int $value): DataResponse {
try {
$this->setCurrentFederatedUser();
$outcome = $this->circleService->updateConfig($circleId, $value);
return new DataResponse($this->serializeArray($outcome));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId, 'value' => $value]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @NoAdminRequired
*
* @param string $circleId
* @param string $singleId
*
* @return DataResponse
* @throws OCSException
*/
public function link(string $circleId, string $singleId): DataResponse {
try {
$this->setCurrentFederatedUser();
$membership = $this->membershipService->getMembership($circleId, $singleId, true);
return new DataResponse($this->serialize($membership));
} catch (Exception $e) {
$this->e($e, ['circleId' => $circleId, 'singleId' => $singleId]);
throw new OCSException($e->getMessage(), (int)$e->getCode());
}
}
/**
* @return void
* @throws FederatedUserException
* @throws FederatedUserNotFoundException
* @throws FrontendException
* @throws InvalidIdException
* @throws RequestBuilderException
* @throws SingleCircleNotFoundException
*/
private function setCurrentFederatedUser(): void {
if (!$this->configService->getAppValueBool(ConfigService::FRONTEND_ENABLED)) {
throw new FrontendException('frontend disabled');
}
$user = $this->userSession->getUser();
$this->federatedUserService->setLocalCurrentUser($user);
}
}
@@ -0,0 +1,612 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2020
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Controller;
use Exception;
use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
use OCA\Circles\Db\CircleRequest;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\JsonNotRequestedException;
use OCA\Circles\Exceptions\UnknownInterfaceException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Federated\FederatedEvent;
use OCA\Circles\Model\Federated\RemoteInstance;
use OCA\Circles\Model\FederatedUser;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\Probes\BasicProbe;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\InterfaceService;
use OCA\Circles\Service\MemberService;
use OCA\Circles\Service\MembershipService;
use OCA\Circles\Service\RemoteDownstreamService;
use OCA\Circles\Service\RemoteStreamService;
use OCA\Circles\Tools\Exceptions\InvalidItemException;
use OCA\Circles\Tools\Exceptions\InvalidOriginException;
use OCA\Circles\Tools\Exceptions\ItemNotFoundException;
use OCA\Circles\Tools\Exceptions\MalformedArrayException;
use OCA\Circles\Tools\Exceptions\SignatoryException;
use OCA\Circles\Tools\Exceptions\SignatureException;
use OCA\Circles\Tools\Exceptions\UnknownTypeException;
use OCA\Circles\Tools\Model\NCSignedRequest;
use OCA\Circles\Tools\Model\SimpleDataStore;
use OCA\Circles\Tools\Traits\TDeserialize;
use OCA\Circles\Tools\Traits\TNCLocalSignatory;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
use OCP\IUserSession;
/**
* Class RemoteController
*
* @package OCA\Circles\Controller
*/
class RemoteController extends Controller {
use TNCLocalSignatory;
use TDeserialize;
/** @var CircleRequest */
private $circleRequest;
/** @var RemoteStreamService */
private $remoteStreamService;
/** @var RemoteDownstreamService */
private $remoteDownstreamService;
/** @var FederatedUserService */
private $federatedUserService;
/** @var CircleService */
private $circleService;
/** @var MemberService */
private $memberService;
/** @var MembershipService */
private $membershipService;
/** @var InterfaceService */
private $interfaceService;
/** @var ConfigService */
private $configService;
/** @var IUserSession */
private $userSession;
/**
* RemoteController constructor.
*
* @param string $appName
* @param IRequest $request
* @param CircleRequest $circleRequest
* @param RemoteStreamService $remoteStreamService
* @param RemoteDownstreamService $remoteDownstreamService
* @param FederatedUserService $federatedUserService
* @param CircleService $circleService
* @param MemberService $memberService
* @param MembershipService $membershipService
* @param InterfaceService $interfaceService
* @param ConfigService $configService
*/
public function __construct(
string $appName,
IRequest $request,
CircleRequest $circleRequest,
RemoteStreamService $remoteStreamService,
RemoteDownstreamService $remoteDownstreamService,
FederatedUserService $federatedUserService,
CircleService $circleService,
MemberService $memberService,
MembershipService $membershipService,
InterfaceService $interfaceService,
ConfigService $configService,
IUserSession $userSession
) {
parent::__construct($appName, $request);
$this->circleRequest = $circleRequest;
$this->remoteStreamService = $remoteStreamService;
$this->remoteDownstreamService = $remoteDownstreamService;
$this->federatedUserService = $federatedUserService;
$this->circleService = $circleService;
$this->memberService = $memberService;
$this->membershipService = $membershipService;
$this->interfaceService = $interfaceService;
$this->configService = $configService;
$this->userSession = $userSession;
$this->setup('app', 'circles');
$this->setupArray('enforceSignatureHeaders', ['digest', 'content-length']);
}
/**
* @PublicPage
* @NoCSRFRequired
*
* @param string $test
*
* @return DataResponse
* @throws NotLoggedInException
* @throws SignatoryException
* @throws UnknownInterfaceException
*/
public function appService(string $test = ''): DataResponse {
try {
$this->publicPageJsonLimited();
} catch (JsonNotRequestedException $e) {
return new DataResponse();
}
$this->interfaceService->setCurrentInterfaceFromRequest($this->request, $test);
$signatory = $this->remoteStreamService->getAppSignatory(false, $this->request->getParam('auth', ''));
return new DataResponse($signatory);
}
/**
* @PublicPage
* @NoCSRFRequired
*
* @return DataResponse
*/
public function event(): DataResponse {
try {
$event = $this->extractEventFromRequest();
} catch (Exception $e) {
return $this->exceptionResponse($e, Http::STATUS_UNAUTHORIZED);
}
try {
$this->remoteDownstreamService->requestedEvent($event);
return new DataResponse($event->getOutcome());
} catch (Exception $e) {
$this->e($e, ['event' => $event->getWrapperToken()]);
return $this->exceptionResponse($e);
}
}
/**
* @PublicPage
* @NoCSRFRequired
*
* @return DataResponse
*/
public function incoming(): DataResponse {
try {
$event = $this->extractEventFromRequest();
} catch (Exception $e) {
$this->e($e);
return $this->exceptionResponse($e, Http::STATUS_UNAUTHORIZED);
}
try {
$this->remoteDownstreamService->incomingEvent($event);
return new DataResponse($this->serialize($event->getResult()));
} catch (Exception $e) {
return $this->exceptionResponse($e);
}
}
/**
* @PublicPage
* @NoCSRFRequired
*
* @return DataResponse
*/
public function test(): DataResponse {
try {
$this->interfaceService->setCurrentInterfaceFromRequest($this->request);
$test = $this->remoteStreamService->incomingSignedRequest();
return new DataResponse($this->serialize($test));
} catch (Exception $e) {
$this->e($e);
return $this->exceptionResponse($e, Http::STATUS_UNAUTHORIZED);
}
}
/**
* @PublicPage
* @NoCSRFRequired
*
* @return DataResponse
*/
public function circles(): DataResponse {
try {
$data = $this->extractDataFromFromRequest();
} catch (Exception $e) {
return $this->exceptionResponse($e, Http::STATUS_UNAUTHORIZED);
}
try {
/** @var Circle $filterCircle */
$filterCircle = $data->gObj('filterCircle');
/** @var Member $filterMember */
$filterMember = $data->gObj('filterMember');
$probe = new CircleProbe();
$probe->setFilterCircle($filterCircle)
->setFilterMember($filterMember)
->addDetail(BasicProbe::DETAILS_POPULATION);
$circles = $this->circleService->getCircles($probe);
return new DataResponse($circles);
} catch (Exception $e) {
return $this->exceptionResponse($e);
}
}
/**
* @PublicPage
* @NoCSRFRequired
*
* @param string $circleId
*
* @return DataResponse
*/
public function circle(string $circleId): DataResponse {
try {
$this->extractDataFromFromRequest();
} catch (Exception $e) {
return $this->exceptionResponse($e, Http::STATUS_UNAUTHORIZED);
}
try {
$circle = $this->circleService->getCircle($circleId);
return new DataResponse($this->serialize($circle));
} catch (Exception $e) {
return $this->exceptionResponse($e);
}
}
/**
* @PublicPage
* @NoCSRFRequired
*
* @param string $circleId
*
* @return DataResponse
*/
public function members(string $circleId): DataResponse {
try {
$this->extractDataFromFromRequest();
} catch (Exception $e) {
return $this->exceptionResponse($e, Http::STATUS_UNAUTHORIZED);
}
try {
$members = $this->memberService->getMembers($circleId);
return new DataResponse($members);
} catch (Exception $e) {
return $this->exceptionResponse($e);
}
}
/**
* ?? TODO: rename /member/ to /federatedUser/ ou /federated/ ?
*
* @PublicPage
* @NoCSRFRequired
*
* @param string $type
* @param string $userId
*
* @return DataResponse
*/
public function member(string $type, string $userId): DataResponse {
try {
$this->extractDataFromFromRequest();
} catch (Exception $e) {
$this->e($e);
return $this->exceptionResponse($e, Http::STATUS_UNAUTHORIZED);
}
try {
// FILTER CIRCLE BASED ON THE CONFIG/FEDERATED_8192 !!
if ($type === Member::$TYPE[Member::TYPE_SINGLE]) {
$federatedUser = $this->federatedUserService->getFederatedUser($userId, Member::TYPE_SINGLE);
} elseif ($type === Member::$TYPE[Member::TYPE_CIRCLE]) {
$federatedUser = $this->federatedUserService->getFederatedUser($userId, Member::TYPE_CIRCLE);
} elseif ($type === Member::$TYPE[Member::TYPE_USER]) {
$federatedUser = $this->federatedUserService->getLocalFederatedUser($userId);
} else {
throw new FederatedUserNotFoundException('Entity not found');
}
return new DataResponse($this->serialize($federatedUser));
} catch (Exception $e) {
return $this->exceptionResponse($e);
}
}
/**
* @PublicPage
* @NoCSRFRequired
*
* @param string $circleId
*
* @return DataResponse
*/
public function inherited(string $circleId): DataResponse {
try {
$this->extractDataFromFromRequest();
} catch (Exception $e) {
return $this->exceptionResponse($e, Http::STATUS_UNAUTHORIZED);
}
try {
$circle = $this->circleService->getCircle($circleId);
return new DataResponse($circle->getInheritedMembers());
} catch (Exception $e) {
return $this->exceptionResponse($e);
}
}
/**
* @PublicPage
* @NoCSRFRequired
*
* @param string $circleId
*
* @return DataResponse
*/
public function memberships(string $circleId): DataResponse {
try {
$this->extractDataFromFromRequest();
} catch (Exception $e) {
return $this->exceptionResponse($e, Http::STATUS_UNAUTHORIZED);
}
try {
$circle = $this->circleService->getCircle($circleId);
return new DataResponse($circle->getMemberships());
} catch (Exception $e) {
return $this->exceptionResponse($e);
}
}
/**
* @return FederatedEvent
* @throws InvalidItemException
* @throws InvalidOriginException
* @throws MalformedArrayException
* @throws SignatoryException
* @throws SignatureException
*/
private function extractEventFromRequest(): FederatedEvent {
$signed = $this->remoteStreamService->incomingSignedRequest();
$this->confirmRemoteInstance($signed);
$event = new FederatedEvent();
$event->import(json_decode($signed->getBody(), true));
$event->setSender($signed->getOrigin());
return $event;
}
/**
* @return SimpleDataStore
* @throws FederatedUserException
* @throws InvalidOriginException
* @throws MalformedArrayException
* @throws SignatoryException
* @throws SignatureException
* @throws UnknownTypeException
*/
private function extractDataFromFromRequest(): SimpleDataStore {
$signed = $this->remoteStreamService->incomingSignedRequest();
$remoteInstance = $this->confirmRemoteInstance($signed);
// There should be no need to confirm the need or the origin of the initiator as $remoteInstance
// already helps filtering request to the database.
// initiator here is only used to play with the visibility, on top of the visibility provided to
// the remote instance based on its type.
$this->federatedUserService->setRemoteInstance($remoteInstance);
$data = new SimpleDataStore();
$store = new SimpleDataStore(json_decode($signed->getBody(), true));
try {
/** @var FederatedUser $initiator */
$initiator = $store->gObj('initiator', FederatedUser::class);
$this->federatedUserService->setCurrentUser($initiator);
} catch (InvalidItemException | ItemNotFoundException $e) {
}
try {
/** @var FederatedUser $initiator */
$filterMember = $store->gObj('filterMember', Member::class);
$data->aObj('filterMember', $filterMember);
} catch (InvalidItemException | ItemNotFoundException $e) {
}
try {
/** @var FederatedUser $initiator */
$filterCircle = $store->gObj('filterCircle', Circle::class);
$data->aObj('filterCircle', $filterCircle);
} catch (InvalidItemException | ItemNotFoundException $e) {
}
return $data;
}
/**
* @param NCSignedRequest $signedRequest
*
* @return RemoteInstance
* @throws SignatoryException
*/
private function confirmRemoteInstance(NCSignedRequest $signedRequest): RemoteInstance {
/** @var RemoteInstance $signatory */
$signatory = $signedRequest->getSignatory();
if (!$signatory instanceof RemoteInstance) {
$this->debug('Signatory is not a known RemoteInstance', ['signedRequest' => $signedRequest]);
throw new SignatoryException('Could not confirm identity');
}
if (!$this->configService->isLocalInstance($signedRequest->getOrigin())
&& $signatory->getType() === RemoteInstance::TYPE_UNKNOWN) {
$this->debug('Could not confirm identity', ['signedRequest' => $signedRequest]);
throw new SignatoryException('Could not confirm identity');
}
$this->interfaceService->setCurrentInterface($signatory->getInterface());
return $signatory;
}
/**
* @param Exception $e
* @param int $httpErrorCode
*
* @return DataResponse
*/
public function exceptionResponse(
Exception $e,
int $httpErrorCode = Http::STATUS_BAD_REQUEST
): DataResponse {
if ($e instanceof FederatedItemException) {
return new DataResponse($this->serialize($e), $e->getStatus());
}
return new DataResponse(
[
'message' => $e->getMessage(),
'code' => $e->getCode()
],
($e->getCode() > 0) ? $e->getCode() : $httpErrorCode
);
}
/**
* use this one if a method from a Controller is only PublicPage when remote client asking for Json
*
* try {
* $this->publicPageJsonLimited();
* return new DataResponse(['test' => 42]);
* } catch (JsonNotRequestedException $e) {}
*
*
* @throws NotLoggedInException
* @throws JsonNotRequestedException
*/
private function publicPageJsonLimited(): void {
if (!$this->jsonRequested()) {
if (!$this->userSession->isLoggedIn()) {
throw new NotLoggedInException();
}
throw new JsonNotRequestedException();
}
}
/**
* @return bool
*/
private function jsonRequested(): bool {
return ($this->areWithinAcceptHeader(
[
'application/json',
'application/ld+json',
'application/activity+json'
]
));
}
/**
* @param array $needles
*
* @return bool
*/
private function areWithinAcceptHeader(array $needles): bool {
$accepts = array_map([$this, 'trimHeader'], explode(',', $this->request->getHeader('Accept')));
foreach ($accepts as $accept) {
if (in_array($accept, $needles)) {
return true;
}
}
return false;
}
/**
* @param string $header
*
* @return string
*/
private function trimHeader(string $header): string {
$header = trim($header);
$pos = strpos($header, ';');
if ($pos === false) {
return $header;
}
return substr($header, 0, $pos);
}
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Cron;
use OCA\Circles\Service\MaintenanceService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJob;
use OCP\BackgroundJob\TimedJob;
class Maintenance extends TimedJob {
private MaintenanceService $maintenanceService;
/**
* @param ITimeFactory $time
* @param MaintenanceService $maintenanceService
*/
public function __construct(ITimeFactory $time, MaintenanceService $maintenanceService) {
parent::__construct($time);
$this->setInterval(10);
$this->setTimeSensitivity(IJob::TIME_SENSITIVE);
$this->maintenanceService = $maintenanceService;
}
/**
* @param $argument
*/
protected function run($argument) {
$this->maintenanceService->runMaintenances();
}
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Cron;
use OCA\Circles\Service\MaintenanceService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJob;
use OCP\BackgroundJob\TimedJob;
class MaintenanceHeavy extends TimedJob {
private MaintenanceService $maintenanceService;
/**
* @param ITimeFactory $time
* @param MaintenanceService $maintenanceService
*/
public function __construct(ITimeFactory $time, MaintenanceService $maintenanceService) {
parent::__construct($time);
$this->setInterval(24 * 3600);
$this->setTimeSensitivity(IJob::TIME_INSENSITIVE);
$this->maintenanceService = $maintenanceService;
}
/**
* @param $argument
*/
protected function run($argument) {
$this->maintenanceService->runMaintenances(true);
}
}
@@ -0,0 +1,97 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Traits\TStringTools;
use OCA\Circles\Exceptions\MemberDoesNotExistException;
class AccountsRequest extends AccountsRequestBuilder {
use TStringTools;
public function getAccountData(string $userId): array {
$qb = $this->getAccountsSelectSql();
$this->limitToDBField($qb, 'uid', $userId);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
return [];
}
return $this->parseAccountsSelectSql($data);
}
/**
* @param string $userId
*
* @deprecated
* @return array
* @throws MemberDoesNotExistException
*/
public function getFromUserId(string $userId): array {
$qb = $this->getAccountsSelectSql();
$this->limitToDBField($qb, 'uid', $userId);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new MemberDoesNotExistException();
}
return $this->parseAccountsSelectSql($data);
}
/**
* @deprecated
* @return array
*/
public function getAll(): array {
$qb = $this->getAccountsSelectSql();
$accounts = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$account = $this->parseAccountsSelectSql($data);
$accounts[$account['userId']] = $account;
}
$cursor->closeCursor();
return $accounts;
}
}
@@ -0,0 +1,114 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Traits\TArrayTools;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* Class AccountsRequestBuilder
*
* @package OCA\Circles\Db
*/
class AccountsRequestBuilder extends DeprecatedRequestBuilder {
use TArrayTools;
/**
* Base of the Sql Insert request for Accounts
*
* @return IQueryBuilder
*/
protected function getAccountsInsertSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::NC_TABLE_ACCOUNTS);
return $qb;
}
/**
* Base of the Sql Update request for Accounts
*
* @return IQueryBuilder
*/
protected function getAccountsUpdateSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->update(self::NC_TABLE_ACCOUNTS);
return $qb;
}
/**
* @return IQueryBuilder
*/
protected function getAccountsSelectSql() {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select('a.uid', 'a.data')
->from(self::NC_TABLE_ACCOUNTS, 'a');
$this->default_select_alias = 'a';
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return IQueryBuilder
*/
protected function getAccountsDeleteSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::NC_TABLE_ACCOUNTS);
return $qb;
}
/**
* @param array $entry
*
* @return array
*/
protected function parseAccountsSelectSql(array $entry): array {
$data = json_decode($entry['data'], true);
if (!is_array($data)) {
$data = [];
}
return [
'userId' => $entry['uid'],
'displayName' => $this->get('displayname.value', $data)
];
}
}
@@ -0,0 +1,101 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Exceptions\GSStatusException;
/**
* @deprecated
* Class CircleProviderRequest
*
* @package OCA\Circles\Db
*/
class CircleProviderRequest extends CircleProviderRequestBuilder {
/**
* @param $userId
* @param $circleUniqueIds
* @param $limit
* @param $offset
*
* @return array
* @throws GSStatusException
*/
public function getFilesForCircles($userId, $circleUniqueIds, $limit, $offset) {
$qb = $this->getCompleteSelectSql();
$this->linkToFileCache($qb, $userId);
$this->limitToPage($qb, $limit, $offset);
$this->limitToCircles($qb, $circleUniqueIds);
$this->linkToMember($qb, $userId, false, 'c');
// $this->leftJoinShareInitiator($qb);
$cursor = $qb->execute();
$object_ids = [];
while ($data = $cursor->fetch()) {
self::editShareFromParentEntry($data);
if (self::isAccessibleResult($data)) {
$object_ids[] = $data['file_source'];
}
}
$cursor->closeCursor();
return $object_ids;
}
/**
* Returns whether the given database result can be interpreted as
* a share with accessible file (not trashed, not deleted)
*
* @param $data
*F
*
* @return bool
*/
protected static function isAccessibleResult($data) {
if ($data['fileid'] === null || $data['path'] === null) {
return false;
}
return (!(explode('/', $data['path'], 2)[0] !== 'files'
&& explode(':', $data['storage_string_id'], 2)[0] === 'home'));
}
/**
* @param $data
*/
protected static function editShareFromParentEntry(&$data) {
if ($data['parent_id'] > 0) {
$data['permissions'] = $data['parent_perms'];
$data['file_target'] = $data['parent_target'];
}
}
}
@@ -0,0 +1,531 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @author Vinicius Cubas Brand <vinicius@eita.org.br>
* @author Daniel Tygel <dtygel@eita.org.br>
*
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use Doctrine\DBAL\Query\QueryBuilder;
use OC;
use OCA\Circles\Model\DeprecatedMember;
use OCP\DB\QueryBuilder\ICompositeExpression;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\NotFoundException;
use OCP\Share\IShare;
/**
* @deprecated
* Class CircleProviderRequestBuilder
*
* @package OCA\Circles\Db
*/
class CircleProviderRequestBuilder extends DeprecatedRequestBuilder {
/**
* returns the SQL request to get a specific share from the fileId and circleId
*
* @param int $fileId
* @param int $circleId
*
* @return IQueryBuilder
*/
protected function findShareParentSql($fileId, $circleId) {
$qb = $this->getBaseSelectSql();
$this->limitToShareParent($qb);
$this->limitToCircles($qb, [$circleId]);
$this->limitToFiles($qb, $fileId);
return $qb;
}
/**
* Limit the request to the given Circles.
*
* @param IQueryBuilder $qb
* @param array $circleUniqueIds
*/
protected function limitToCircles(IQueryBuilder $qb, $circleUniqueIds) {
if (!is_array($circleUniqueIds)) {
$circleUniqueIds = [$circleUniqueIds];
}
$expr = $qb->expr();
$pf = ($qb->getType() === QueryBuilder::SELECT) ? 's.' : '';
$qb->andWhere(
$expr->in(
$pf . 'share_with',
$qb->createNamedParameter($circleUniqueIds, IQueryBuilder::PARAM_STR_ARRAY)
)
);
}
/**
* Limit the request to the Share by its Id.
*
* @param IQueryBuilder $qb
* @param $shareId
*/
protected function limitToShare(IQueryBuilder $qb, $shareId) {
$expr = $qb->expr();
$pf = ($qb->getType() === QueryBuilder::SELECT) ? 's.' : '';
$qb->andWhere($expr->eq($pf . 'id', $qb->createNamedParameter($shareId)));
}
/**
* Limit the request to the top share (no children)
*
* @param IQueryBuilder $qb
*/
protected function limitToShareParent(IQueryBuilder $qb) {
$expr = $qb->expr();
$qb->andWhere($expr->isNull('parent'));
}
/**
* limit the request to the children of a share
*
* @param IQueryBuilder $qb
* @param $userId
* @param int $parentId
*/
protected function limitToShareChildren(IQueryBuilder $qb, $userId, $parentId = -1) {
$expr = $qb->expr();
$qb->andWhere($expr->eq('share_with', $qb->createNamedParameter($userId)));
if ($parentId > -1) {
$qb->andWhere($expr->eq('parent', $qb->createNamedParameter($parentId)));
} else {
$qb->andWhere($expr->isNotNull('parent'));
}
}
/**
* limit the request to the share itself AND its children.
* perfect if you want to delete everything related to a share
*
* @param IQueryBuilder $qb
* @param $circleId
*/
protected function limitToShareAndChildren(IQueryBuilder $qb, $circleId) {
$expr = $qb->expr();
$pf = ($qb->getType() === QueryBuilder::SELECT) ? 's.' : '';
$qb->andWhere(
$expr->orX(
$expr->eq($pf . 'parent', $qb->createNamedParameter($circleId)),
$expr->eq($pf . 'id', $qb->createNamedParameter($circleId))
)
);
}
/**
* limit the request to a fileId.
*
* @param IQueryBuilder $qb
* @param $files
*/
protected function limitToFiles(IQueryBuilder $qb, $files) {
if (!is_array($files)) {
$files = [$files];
}
$expr = $qb->expr();
$pf = ($qb->getType() === QueryBuilder::SELECT) ? 's.' : '';
$qb->andWhere(
$expr->in(
$pf . 'file_source',
$qb->createNamedParameter($files, IQueryBuilder::PARAM_INT_ARRAY)
)
);
}
/**
* @param IQueryBuilder $qb
* @param int $limit
* @param int $offset
*/
protected function limitToPage(IQueryBuilder $qb, $limit = -1, $offset = 0) {
if ($limit !== -1) {
$qb->setMaxResults($limit);
}
$qb->setFirstResult($offset);
}
/**
* limit the request to a userId
*
* @param IQueryBuilder $qb
* @param string $userId
* @param bool $reShares
*/
protected function limitToShareOwner(IQueryBuilder $qb, $userId, $reShares = false) {
$expr = $qb->expr();
$pf = ($qb->getType() === QueryBuilder::SELECT) ? 's.' : '';
if ($reShares === false) {
$qb->andWhere($expr->eq($pf . 'uid_initiator', $qb->createNamedParameter($userId)));
} else {
$qb->andWhere(
$expr->orX(
$expr->eq($pf . 'uid_owner', $qb->createNamedParameter($userId)),
$expr->eq($pf . 'uid_initiator', $qb->createNamedParameter($userId))
)
);
}
}
/**
* link circle field
*
* @param IQueryBuilder $qb
* @param int $shareId
*
* @deprecated
*
*/
protected function linkCircleField(IQueryBuilder $qb, $shareId = -1) {
$expr = $qb->expr();
$qb->from(DeprecatedRequestBuilder::TABLE_CIRCLES, 'c');
$tmpOrX = $expr->eq('s.share_with', 'c.unique_id');
if ($shareId === -1) {
$qb->andWhere($tmpOrX);
return;
}
$qb->andWhere(
$expr->orX(
$tmpOrX,
$expr->eq('s.parent', $qb->createNamedParameter($shareId))
)
);
}
/**
* @param IQueryBuilder $qb
*/
protected function linkToCircleOwner(IQueryBuilder $qb) {
$expr = $qb->expr();
$qb->selectAlias('mo.user_id', 'circle_owner');
$qb->leftJoin(
'c', DeprecatedRequestBuilder::TABLE_MEMBERS, 'mo', $expr->andX(
$expr->eq('mo.circle_id', 'c.unique_id'),
$expr->eq('mo.user_type', $qb->createNamedParameter(DeprecatedMember::TYPE_USER)),
$expr->eq('mo.level', $qb->createNamedParameter(DeprecatedMember::LEVEL_OWNER))
)
);
}
/**
* Link to member (userId) of circle
*
* @param IQueryBuilder $qb
* @param string $userId
* @param bool $groupMemberAllowed
* @param string $aliasCircles
*/
protected function linkToMember(
IQueryBuilder $qb, string $userId, bool $groupMemberAllowed, string $aliasCircles
) {
$qb->from(DeprecatedRequestBuilder::TABLE_MEMBERS, 'mcm');
$expr = $qb->expr();
$orX = $expr->orX();
$orX->add($this->exprLinkToMemberAsCircleMember($qb, $userId, 'mcm', $aliasCircles));
if ($groupMemberAllowed) {
$orX->add($this->exprLinkToMemberAsGroupMember($qb, $userId, 'mcm', $aliasCircles));
}
$qb->andWhere($orX);
}
/**
* generate CompositeExpression to link to a Member as a Real Circle Member
*
* @param IQueryBuilder $qb
* @param string $userId
* @param string $aliasM
* @param string $aliasC
*
* @return ICompositeExpression
*/
private function exprLinkToMemberAsCircleMember(
IQueryBuilder $qb, string $userId, string $aliasM, string $aliasC
): ICompositeExpression {
$expr = $qb->expr();
$andX = $expr->andX();
$andX->add($expr->eq($aliasM . '.user_id', $qb->createNamedParameter($userId)));
$andX->add($expr->eq($aliasM . '.circle_id', $aliasC . '.unique_id'));
$andX->add($expr->gte($aliasM . '.level', $qb->createNamedParameter(DeprecatedMember::LEVEL_MEMBER)));
$andX->add($expr->eq($aliasM . '.instance', $qb->createNamedParameter('')));
$andX->add($expr->eq($aliasM . '.user_type', $qb->createNamedParameter(DeprecatedMember::TYPE_USER)));
return $andX;
}
/**
* generate CompositeExpression to link to a Member as a Group Member (core NC)
*
* @param IQueryBuilder $qb
* @param string $userId
* @param string $aliasM
* @param string $aliasC
*
* @return ICompositeExpression
*/
private function exprLinkToMemberAsGroupMember(
IQueryBuilder $qb, string $userId, string $aliasM, string $aliasC
) {
$expr = $qb->expr();
$qb->leftJoin(
$aliasM, self::NC_TABLE_GROUP_USER, 'ncgu',
$expr->eq('ncgu.uid', $qb->createNamedParameter($userId))
);
$andX = $expr->andX();
$andX->add($expr->eq($aliasM . '.user_id', 'ncgu.gid'));
$andX->add($expr->eq($aliasM . '.user_type', $qb->createNamedParameter(DeprecatedMember::TYPE_GROUP)));
$andX->add($expr->eq($aliasM . '.instance', $qb->createNamedParameter('')));
$andX->add($expr->eq($aliasM . '.circle_id', $aliasC . '.unique_id'));
$andX->add($expr->gte($aliasM . '.level', $qb->createNamedParameter(DeprecatedMember::LEVEL_MEMBER)));
return $andX;
}
/**
* Link to all members of circle
*
* @param IQueryBuilder $qb
*/
protected function joinCircleMembers(IQueryBuilder $qb) {
$expr = $qb->expr();
$qb->addSelect('m.user_id')
->from(DeprecatedRequestBuilder::TABLE_MEMBERS, 'm')
->andWhere(
$expr->andX(
$expr->eq('s.share_with', 'm.circle_id'),
$expr->eq('m.user_type', $qb->createNamedParameter(DeprecatedMember::TYPE_USER))
)
);
}
/**
* Link to storage/filecache
*
* @param IQueryBuilder $qb
* @param string $userId
*/
protected function linkToFileCache(IQueryBuilder $qb, $userId) {
$expr = $qb->expr();
$qb->leftJoin('s', 'filecache', 'f', $expr->eq('s.file_source', 'f.fileid'))
->leftJoin('f', 'storages', 'st', $expr->eq('f.storage', 'st.numeric_id'))
->leftJoin(
's', 'share', 's2', $expr->andX(
$expr->eq('s.id', 's2.parent'),
$expr->eq('s2.share_with', $qb->createNamedParameter($userId))
)
);
$qb->selectAlias('s2.id', 'parent_id');
$qb->selectAlias('s2.file_target', 'parent_target');
$qb->selectAlias('s2.permissions', 'parent_perms');
}
/**
* add share to the database and return the ID
*
* @param IShare $share
*
* @return IQueryBuilder
* @throws NotFoundException
*/
protected function getBaseInsertSql($share) {
$qb = $this->dbConnection->getQueryBuilder();
$hasher = OC::$server->getHasher();
$password = ($share->getPassword() !== null) ? $hasher->hash($share->getPassword()) : '';
$qb->insert('share')
->setValue('share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE))
->setValue('item_type', $qb->createNamedParameter($share->getNodeType()))
->setValue('item_source', $qb->createNamedParameter($share->getNodeId()))
->setValue('file_source', $qb->createNamedParameter($share->getNodeId()))
->setValue('file_target', $qb->createNamedParameter($share->getTarget()))
->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()))
->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED))
->setValue('password', $qb->createNamedParameter($password))
->setValue('permissions', $qb->createNamedParameter($share->getPermissions()))
->setValue('token', $qb->createNamedParameter($share->getToken()))
->setValue('stime', (string)$qb->createFunction('UNIX_TIMESTAMP()'));
return $qb;
}
/**
* generate and return a base sql request.
*
* @param int $shareId
*
* @return IQueryBuilder
*/
protected function getBaseSelectSql($shareId = -1) {
$qb = $this->dbConnection->getQueryBuilder();
$qb->select(
's.id', 's.share_type', 's.share_with', 's.uid_owner', 's.uid_initiator',
's.parent', 's.item_type', 's.item_source', 's.item_target', 's.permissions', 's.stime',
's.accepted', 's.expiration', 's.token', 's.mail_send', 'c.type AS circle_type',
'c.name AS circle_name', 'c.alt_name AS circle_alt_name'
);
$this->linkToCircleOwner($qb);
$this->joinShare($qb);
// TODO: Left-join circle and REMOVE this line
$this->linkCircleField($qb, $shareId);
return $qb;
}
/**
* Generate and return a base sql request
* This one should be used to retrieve a complete list of users (ie. access list).
*
* @return IQueryBuilder
*/
protected function getAccessListBaseSelectSql() {
$qb = $this->dbConnection->getQueryBuilder();
$this->joinCircleMembers($qb);
$this->joinShare($qb);
return $qb;
}
/**
* @return IQueryBuilder
*/
protected function getCompleteSelectSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->selectDistinct('s.id')
->addSelect(
's.*', 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage',
'f.path_hash', 'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart',
'f.size', 'f.mtime', 'f.storage_mtime', 'f.encrypted', 'f.unencrypted_size',
'f.etag', 'f.checksum', 'c.type AS circle_type', 'c.name AS circle_name',
'c.alt_name AS circle_alt_name'
)
->selectAlias('st.id', 'storage_string_id');
$this->linkToCircleOwner($qb);
$this->joinShare($qb);
$this->linkCircleField($qb);
return $qb;
}
/**
* @param IQueryBuilder $qb
*/
private function joinShare(IQueryBuilder $qb) {
$expr = $qb->expr();
$qb->addSelect('s.file_source', 's.file_target');
$qb->from('share', 's')
->andWhere($expr->eq('s.share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE)))
->andWhere(
$expr->orX(
$expr->eq('s.item_type', $qb->createNamedParameter('file')),
$expr->eq('s.item_type', $qb->createNamedParameter('folder'))
)
);
}
/**
* generate and return a base sql request.
*
* @return IQueryBuilder
*/
protected function getBaseDeleteSql() {
$qb = $this->dbConnection->getQueryBuilder();
$expr = $qb->expr();
$qb->delete('share')
->where($expr->eq('share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE)));
return $qb;
}
/**
* generate and return a base sql request.
*
* @return IQueryBuilder
*/
protected function getBaseUpdateSql() {
$qb = $this->dbConnection->getQueryBuilder();
$expr = $qb->expr();
$qb->update('share')
->where($expr->eq('share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE)));
return $qb;
}
}
@@ -0,0 +1,521 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\IFederatedUser;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\FederatedUser;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Model\Probes\DataProbe;
/**
* Class CircleRequest
*
* @package OCA\Circles\Db
*/
class CircleRequest extends CircleRequestBuilder {
/**
* @param Circle $circle
*
* @throws InvalidIdException
*/
public function save(Circle $circle): void {
$this->confirmValidId($circle->getSingleId());
$qb = $this->getCircleInsertSql();
$qb->setValue('unique_id', $qb->createNamedParameter($circle->getSingleId()))
->setValue('name', $qb->createNamedParameter($circle->getName()))
->setValue('source', $qb->createNamedParameter($circle->getSource()))
->setValue('display_name', $qb->createNamedParameter($circle->getDisplayName()))
->setValue('sanitized_name', $qb->createNamedParameter($circle->getSanitizedName()))
->setValue('description', $qb->createNamedParameter($circle->getDescription()))
->setValue('contact_addressbook', $qb->createNamedParameter($circle->getContactAddressBook()))
->setValue('contact_groupname', $qb->createNamedParameter($circle->getContactGroupName()))
->setValue('settings', $qb->createNamedParameter(json_encode($circle->getSettings())))
->setValue('config', $qb->createNamedParameter($circle->getConfig()));
$qb->execute();
}
/**
* @param Circle $circle
*/
public function edit(Circle $circle): void {
$qb = $this->getCircleUpdateSql();
$qb->set('name', $qb->createNamedParameter($circle->getName()))
->set('display_name', $qb->createNamedParameter($circle->getDisplayName()))
->set('sanitized_name', $qb->createNamedParameter($circle->getSanitizedName()))
->set('description', $qb->createNamedParameter($circle->getDescription()));
$qb->limitToUniqueId($circle->getSingleId());
$qb->execute();
}
/**
* @param Circle $circle
*/
public function update(Circle $circle) {
$qb = $this->getCircleUpdateSql();
$qb->set('name', $qb->createNamedParameter($circle->getName()))
->set('display_name', $qb->createNamedParameter($circle->getDisplayName()))
->set('description', $qb->createNamedParameter($circle->getDescription()))
->set('settings', $qb->createNamedParameter(json_encode($circle->getSettings())))
->set('config', $qb->createNamedParameter($circle->getConfig()));
$qb->limitToUniqueId($circle->getSingleId());
$qb->execute();
}
/**
* @param Circle $circle
*
* @throws InvalidIdException
*/
public function insertOrUpdate(Circle $circle): void {
try {
$this->getCircle($circle->getSingleId());
$this->update($circle);
} catch (CircleNotFoundException $e) {
$this->save($circle);
}
}
/**
* @param string $singleId
* @param string $displayName
*/
public function updateDisplayName(string $singleId, string $displayName): void {
$qb = $this->getCircleUpdateSql();
$qb->set('display_name', $qb->createNamedParameter($displayName));
$qb->limitToUniqueId($singleId);
$qb->execute();
}
/**
* @param Circle $circle
*/
public function updateConfig(Circle $circle) {
$qb = $this->getCircleUpdateSql();
$qb->set('config', $qb->createNamedParameter($circle->getConfig()));
$qb->limitToUniqueId($circle->getSingleId());
$qb->execute();
}
/**
* @param Circle $circle
*/
public function updateSettings(Circle $circle) {
$qb = $this->getCircleUpdateSql();
$qb->set('settings', $qb->createNamedParameter(json_encode($circle->getSettings())));
$qb->limitToUniqueId($circle->getSingleId());
$qb->execute();
}
/**
* @param IFederatedUser|null $initiator
* @param CircleProbe $probe
*
* @return Circle[]
* @throws RequestBuilderException
*/
public function getCircles(?IFederatedUser $initiator, CircleProbe $probe): array {
$qb = $this->getCircleSelectSql();
$qb->leftJoinOwner(CoreQueryBuilder::CIRCLE);
$qb->setOptions(
[CoreQueryBuilder::CIRCLE],
array_merge(
$probe->getAsOptions(),
[
'getData' => true,
'initiatorDirectMember' => true
]
)
);
$qb->filterCircles(CoreQueryBuilder::CIRCLE, $probe);
if (!is_null($initiator)) {
$qb->limitToInitiator(CoreQueryBuilder::CIRCLE, $initiator);
$qb->orderBy($qb->generateAlias(CoreQueryBuilder::CIRCLE, CoreQueryBuilder::INITIATOR) . '.level', 'desc');
$qb->addOrderBy(CoreQueryBuilder::CIRCLE . '.display_name', 'asc');
}
if ($probe->hasFilterMember()) {
$qb->limitToDirectMembership(CoreQueryBuilder::CIRCLE, $probe->getFilterMember());
}
if ($probe->hasFilterCircle()) {
$qb->filterCircleDetails($probe->getFilterCircle());
}
if ($probe->hasFilterRemoteInstance()) {
$qb->limitToRemoteInstance(CoreQueryBuilder::CIRCLE, $probe->getFilterRemoteInstance(), false);
}
$qb->chunk($probe->getItemsOffset(), $probe->getItemsLimit());
return $this->getItemsFromRequest($qb);
}
/**
* get data about single Circle.
*
* - CircleProbe is used to confirm the visibility of the targeted circle,
* - DataProbe is used to define the complexity of the data to be returned for each entry of the list
*
* @param string $singleId
* @param IFederatedUser|null $initiator
* @param CircleProbe $circleProbe
* @param DataProbe $dataProbe
*
* @return Circle
* @throws CircleNotFoundException
* @throws RequestBuilderException
*/
public function probeCircle(
string $singleId,
?IFederatedUser $initiator,
CircleProbe $circleProbe,
DataProbe $dataProbe
): Circle {
$qb = $this->buildProbeCircle($initiator, $circleProbe, $dataProbe);
$qb->limit('unique_id', $singleId);
return $this->getItemFromRequest($qb);
}
/**
* get data about multiple Circles.
*
* - CircleProbe is used to define the list of circles to be returned by the method,
* - DataProbe is used to define the complexity of the data to be returned for each entry of the list
*
* @param IFederatedUser|null $initiator
* @param CircleProbe $circleProbe
* @param DataProbe $dataProbe
*
* @return Circle[]
* @throws RequestBuilderException
*/
public function probeCircles(
?IFederatedUser $initiator,
CircleProbe $circleProbe,
DataProbe $dataProbe
): array {
$qb = $this->buildProbeCircle($initiator, $circleProbe, $dataProbe);
$qb->chunk($circleProbe->getItemsOffset(), $circleProbe->getItemsLimit());
return $this->getItemsFromRequest($qb);
}
/**
* @param IFederatedUser|null $initiator
* @param CircleProbe $circleProbe
* @param DataProbe $dataProbe
*
* @return CoreQueryBuilder
* @throws RequestBuilderException
*/
private function buildProbeCircle(
?IFederatedUser $initiator,
CircleProbe $circleProbe,
DataProbe $dataProbe
): CoreQueryBuilder {
$qb = $this->getCircleSelectSql();
if (!$dataProbe->has(DataProbe::MEMBERSHIPS)) {
$dataProbe->add(DataProbe::MEMBERSHIPS);
}
$qb->setSqlPath(CoreQueryBuilder::CIRCLE, $dataProbe->getPath())
->setOptions([CoreQueryBuilder::CIRCLE], $circleProbe->getAsOptions())
->filterCircles(CoreQueryBuilder::CIRCLE, $circleProbe);
if ($circleProbe->hasFilterCircle()) {
$qb->filterCircleDetails($circleProbe->getFilterCircle());
}
$qb->leftJoinOwner(CoreQueryBuilder::CIRCLE);
$qb->innerJoinMembership($circleProbe, CoreQueryBuilder::CIRCLE);
$aliasMembership = $qb->generateAlias(CoreQueryBuilder::CIRCLE, CoreQueryBuilder::MEMBERSHIPS);
$limit = $qb->expr()->orX();
if (is_null($initiator)) {
// to get unique result, enforce a limit on level=owner
$limit->add($qb->exprLimitInt('level', Member::LEVEL_OWNER, $aliasMembership));
} else {
$limit->add(
$qb->exprLimit(
'single_id',
$initiator->getSingleId(),
$aliasMembership
)
);
$qb->completeProbeWithInitiator(CoreQueryBuilder::CIRCLE, 'single_id', $aliasMembership);
}
$qb->andWhere($limit);
$qb->resetSqlPath();
return $qb;
}
/**
* @param array $circleIds
*
* @return array
* @throws RequestBuilderException
*/
public function getCirclesByIds(array $circleIds): array {
$qb = $this->getCircleSelectSql();
$qb->setOptions(
[CoreQueryBuilder::CIRCLE], ['getData' => true, 'minimumLevel' => Member::LEVEL_NONE]
);
$qb->limitInArray('unique_id', $circleIds);
// $qb->filterCircles(CoreQueryBuilder::CIRCLE, $filter);
$qb->leftJoinOwner(CoreQueryBuilder::CIRCLE);
return $this->getItemsFromRequest($qb);
}
/**
* @param string $id
* @param IFederatedUser|null $initiator
* @param CircleProbe|null $probe
*
* @return Circle
* @throws CircleNotFoundException
* @throws RequestBuilderException
*/
public function getCircle(
string $id,
?IFederatedUser $initiator = null,
?CircleProbe $probe = null
): Circle {
if (is_null($probe)) {
$probe = new CircleProbe();
$probe->includeSystemCircles()
->emulateVisitor();
}
$qb = $this->getCircleSelectSql(CoreQueryBuilder::CIRCLE, true);
$qb->setOptions(
[CoreQueryBuilder::CIRCLE],
array_merge(
$probe->getAsOptions(),
[
'getData' => true,
'initiatorDirectMember' => true
]
)
);
$qb->limitToUniqueId($id);
$qb->filterCircles(CoreQueryBuilder::CIRCLE, $probe);
$qb->leftJoinOwner(CoreQueryBuilder::CIRCLE);
// $qb->setOptions(
// [CoreRequestBuilder::CIRCLE, CoreRequestBuilder::INITIATOR], [
// 'mustBeMember' => false,
// 'viewableAsVisitor' => true
// ]
// );
if (!is_null($initiator)) {
$qb->limitToInitiator(CoreQueryBuilder::CIRCLE, $initiator);
}
if ($probe->hasFilterRemoteInstance()) {
$qb->limitToRemoteInstance(CoreQueryBuilder::CIRCLE, $probe->getFilterRemoteInstance(), false);
}
return $this->getItemFromRequest($qb);
}
/**
* @param string $singleId
*
* @return FederatedUser
* @throws OwnerNotFoundException
* @throws RequestBuilderException
* @throws FederatedUserNotFoundException
*/
public function getFederatedUserBySingleId(string $singleId): FederatedUser {
$qb = $this->getCircleSelectSql(CoreQueryBuilder::CIRCLE, true);
$qb->limitToUniqueId($singleId);
$qb->leftJoinOwner(CoreQueryBuilder::CIRCLE);
try {
$circle = $this->getItemFromRequest($qb);
} catch (CircleNotFoundException $e) {
throw new FederatedUserNotFoundException('singleId not found');
}
$federatedUser = new FederatedUser();
$federatedUser->importFromCircle($circle);
return $federatedUser;
}
/**
* method that return the single-user Circle based on a FederatedUser.
*
* @param IFederatedUser $initiator
*
* @return Circle
* @throws SingleCircleNotFoundException
* @throws RequestBuilderException
*/
public function getSingleCircle(IFederatedUser $initiator): Circle {
$qb = $this->getCircleSelectSql(CoreQueryBuilder::SINGLE, true);
if ($initiator instanceof FederatedUser) {
$member = new Member();
$member->importFromIFederatedUser($initiator);
$member->setLevel(Member::LEVEL_OWNER);
} else {
$member = clone $initiator;
}
$qb->limitToDirectMembership(CoreQueryBuilder::SINGLE, $member);
$qb->limitToConfigFlag(Circle::CFG_SINGLE);
try {
return $this->getItemFromRequest($qb);
} catch (CircleNotFoundException $e) {
throw new SingleCircleNotFoundException();
}
}
/**
* @param Circle $circle
* @param IFederatedUser|null $initiator
*
* @return Circle
* @throws CircleNotFoundException
* @throws RequestBuilderException
*/
public function searchCircle(Circle $circle, ?IFederatedUser $initiator = null): Circle {
$qb = $this->getCircleSelectSql();
$qb->leftJoinOwner(CoreQueryBuilder::CIRCLE);
if ($circle->getName() !== '') {
$qb->limitToName($circle->getName());
}
if ($circle->getDisplayName() !== '') {
$qb->limitToDisplayName($circle->getDisplayName());
}
if ($circle->getSanitizedName() !== '') {
$qb->limitToSanitizedName($circle->getSanitizedName());
}
if ($circle->getConfig() > 0) {
$qb->limitToConfig($circle->getConfig());
}
if ($circle->getSource() > 0) {
$qb->limitToSource($circle->getSource());
}
if ($circle->hasOwner()) {
$aliasOwner = $qb->generateAlias(CoreQueryBuilder::CIRCLE, CoreQueryBuilder::OWNER);
$qb->filterDirectMembership($aliasOwner, $circle->getOwner());
}
if (!is_null($initiator)) {
$qb->setOptions(
[CoreQueryBuilder::CIRCLE],
[
'getData' => true,
'initiatorDirectMember' => true
]
);
$qb->limitToInitiator(CoreQueryBuilder::CIRCLE, $initiator);
}
return $this->getItemFromRequest($qb);
}
/**
* @return Circle[]
* @throws RequestBuilderException
*/
public function getFederated(): array {
$qb = $this->getCircleSelectSql();
$qb->limitToConfigFlag(Circle::CFG_FEDERATED, CoreQueryBuilder::CIRCLE);
$qb->leftJoinOwner(CoreQueryBuilder::CIRCLE);
return $this->getItemsFromRequest($qb);
}
/**
* @param Circle $circle
*/
public function delete(Circle $circle): void {
$qb = $this->getCircleDeleteSql();
$qb->limitToUniqueId($circle->getSingleId());
$qb->execute();
}
/**
* @param IFederatedUser $federatedUser
*/
public function deleteFederatedUser(IFederatedUser $federatedUser): void {
$qb = $this->getCircleDeleteSql();
$qb->limitToUniqueId($federatedUser->getSingleId());
$qb->execute();
}
}
@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Exceptions\RowNotFoundException;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Model\Circle;
/**
* Class CircleRequestBuilder
*
* @package OCA\Circles\Db
*/
class CircleRequestBuilder extends CoreRequestBuilder {
/**
* @return CoreQueryBuilder
*/
protected function getCircleInsertSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->insert(self::TABLE_CIRCLE)
->setValue('creation', $qb->createNamedParameter($this->timezoneService->getUTCDate()));
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getCircleUpdateSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->update(self::TABLE_CIRCLE);
return $qb;
}
/**
* @param string $alias
* @param bool $single
*
* @return CoreQueryBuilder
*/
protected function getCircleSelectSql(
string $alias = CoreQueryBuilder::CIRCLE,
bool $single = false
): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->generateSelect(self::TABLE_CIRCLE, self::$tables[self::TABLE_CIRCLE], $alias);
if (!$single) {
$qb->orderBy($alias . '.creation', 'asc');
}
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return CoreQueryBuilder
*/
protected function getCircleDeleteSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->delete(self::TABLE_CIRCLE);
return $qb;
}
/**
* @param CoreQueryBuilder $qb
*
* @return Circle
* @throws CircleNotFoundException
*/
public function getItemFromRequest(CoreQueryBuilder $qb): Circle {
/** @var Circle $circle */
try {
$circle = $qb->asItem(Circle::class);
} catch (RowNotFoundException $e) {
throw new CircleNotFoundException('Circle not found');
} catch (\Exception $e) {
throw new \Exception($qb->getSQL());
}
return $circle;
}
/**
* @param CoreQueryBuilder $qb
*
* @return Circle[]
*/
public function getItemsFromRequest(CoreQueryBuilder $qb): array {
/** @var Circle[] $result */
return $qb->asItems(Circle::class);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,320 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use Exception;
use OC\DB\Connection;
use OC\DB\SchemaWrapper;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\TimezoneService;
use OCP\Share\IShare;
/**
* Class CoreQueryBuilder
*
* @package OCA\Circles\Db
*/
class CoreRequestBuilder {
public const TABLE_SHARE = 'share';
public const TABLE_FILE_CACHE = 'filecache';
public const TABLE_STORAGES = 'storages';
public const TABLE_CIRCLE = 'circles_circle';
public const TABLE_MEMBER = 'circles_member';
public const TABLE_MEMBERSHIP = 'circles_membership';
public const TABLE_REMOTE = 'circles_remote';
public const TABLE_EVENT = 'circles_event';
public const TABLE_MOUNT = 'circles_mount';
public const TABLE_MOUNTPOINT = 'circles_mountpoint';
// wip
public const TABLE_SHARE_LOCK = 'circles_share_lock';
public const TABLE_TOKEN = 'circles_token';
public const TABLE_GSSHARES = 'circle_gsshares'; // rename ?
public const TABLE_GSSHARES_MOUNTPOINT = 'circle_gsshares_mp'; // rename ?
public const NC_TABLE_ACCOUNTS = 'accounts';
public const NC_TABLE_GROUP_USER = 'group_user';
/** @var array */
public static $tables = [
self::TABLE_CIRCLE => [
'unique_id',
'name',
'display_name',
'sanitized_name',
'source',
'description',
'settings',
'config',
'contact_addressbook',
'contact_groupname',
'creation'
],
self::TABLE_MEMBER => [
'circle_id',
'member_id',
'single_id',
'user_id',
'instance',
'user_type',
'level',
'status',
'note',
'contact_id',
'cached_name',
'cached_update',
'contact_meta',
'joined'
],
self::TABLE_MEMBERSHIP => [
'single_id',
'circle_id',
'level',
'inheritance_first',
'inheritance_last',
'inheritance_path',
'inheritance_depth'
],
self::TABLE_REMOTE => [
'id',
'type',
'interface',
'uid',
'instance',
'href',
'item',
'creation'
],
self::TABLE_EVENT => [
'token',
'event',
'result',
'instance',
'interface',
'severity',
'retry',
'status',
'creation'
],
self::TABLE_MOUNT => [
'id',
'mount_id',
'circle_id',
'single_id',
'token',
'parent',
'mountpoint',
'mountpoint_hash'
],
self::TABLE_MOUNTPOINT => [],
self::TABLE_SHARE_LOCK => [],
self::TABLE_TOKEN => [
'id',
'share_id',
'circle_id',
'single_id',
'member_id',
'token',
'password',
'accepted'
],
self::TABLE_GSSHARES => [],
self::TABLE_GSSHARES_MOUNTPOINT => []
];
public static $outsideTables = [
self::TABLE_SHARE => [
'id',
'share_type',
'share_with',
'uid_owner',
'uid_initiator',
'parent',
'item_type',
'item_source',
'item_target',
'file_source',
'file_target',
'permissions',
'attributes',
'stime',
'accepted',
'expiration',
'token',
'mail_send'
],
self::TABLE_FILE_CACHE => [
'fileid',
'path',
'permissions',
'storage',
'path_hash',
'parent',
'name',
'mimetype',
'mimepart',
'size',
'mtime',
'storage_mtime',
'encrypted',
'unencrypted_size',
'etag',
'checksum'
],
self::TABLE_STORAGES => [
'id'
]
];
/** @var TimezoneService */
protected $timezoneService;
/** @var ConfigService */
protected $configService;
/**
* CoreQueryBuilder constructor.
*
* @param TimezoneService $timezoneService
* @param ConfigService $configService
*/
public function __construct(TimezoneService $timezoneService, ConfigService $configService) {
$this->timezoneService = $timezoneService;
$this->configService = $configService;
}
/**
* @return CoreQueryBuilder
*/
public function getQueryBuilder(): CoreQueryBuilder {
return new CoreQueryBuilder();
}
/**
* @param array $ids
*
* @throws InvalidIdException
*/
public function confirmValidIds(array $ids): void {
foreach ($ids as $id) {
$this->confirmValidId($id);
}
}
/**
* @param string $id
*
* @throws InvalidIdException
*/
public function confirmValidId(string $id): void {
if (strlen($id) < 14) {
throw new InvalidIdException();
}
}
/**
* @param bool $shares
*/
public function cleanDatabase(bool $shares = false): void {
foreach (array_keys(self::$tables) as $table) {
$qb = $this->getQueryBuilder();
try {
$qb->delete($table);
$qb->execute();
} catch (Exception $e) {
}
}
if ($shares) {
$qb = $this->getQueryBuilder();
$expr = $qb->expr();
$qb->delete(self::TABLE_SHARE);
$qb->where($expr->eq('share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE)));
$qb->execute();
}
}
public function uninstall(): void {
$this->uninstallAppTables();
$this->uninstallFromMigrations();
$this->uninstallFromJobs();
$this->configService->unsetAppConfig();
}
/**
* this just empty all tables from the app.
*/
public function uninstallAppTables() {
$dbConn = \OC::$server->get(Connection::class);
$schema = new SchemaWrapper($dbConn);
foreach (array_keys(self::$tables) as $table) {
if ($schema->hasTable($table)) {
$schema->dropTable($table);
}
}
$schema->performDropTableCalls();
}
/**
*
*/
public function uninstallFromMigrations() {
$qb = $this->getQueryBuilder();
$qb->delete('migrations');
$qb->limit('app', 'circles');
$qb->unlike('version', '001%');
$qb->execute();
}
/**
*
*/
public function uninstallFromJobs() {
$qb = $this->getQueryBuilder();
// $qb->delete('jobs');
// $qb->where($this->exprLimitToDBField($qb, 'class', 'OCA\Circles\', true, true));
// $qb->execute();
}
}
@@ -0,0 +1,410 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Exceptions\CircleDoesNotExistException;
use OCA\Circles\Exceptions\ConfigNoCircleAvailableException;
use OCA\Circles\Exceptions\GSStatusException;
use OCA\Circles\Model\DeprecatedCircle;
use OCA\Circles\Model\DeprecatedMember;
class DeprecatedCirclesRequest extends DeprecatedCirclesRequestBuilder {
/**
* forceGetCircle();
*
* returns data of a circle from its Id.
*
* WARNING: This function does not filters data regarding the current user/viewer.
* In case of interaction with users, Please use getCircle() instead.
*
* @param string $circleUniqueId
* @param bool $allSettings
*
* @return DeprecatedCircle
* @throws CircleDoesNotExistException
*/
public function forceGetCircle($circleUniqueId, bool $allSettings = false) {
$qb = $this->getCirclesSelectSql();
$this->leftJoinOwner($qb, '');
$this->limitToUniqueId($qb, $circleUniqueId);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new CircleDoesNotExistException($this->l10n->t('Circle not found'));
}
return $this->parseCirclesSelectSql($data, $allSettings);
}
/**
* forceGetCircles();
*
* returns data of a all circles.
*
* WARNING: This function does not filters data regarding the current user/viewer.
* In case of interaction with users, Please use getCircles() instead.
*
* @param string $ownerId
*
* @return DeprecatedCircle[]
*/
public function forceGetCircles(string $ownerId = '') {
$qb = $this->getCirclesSelectSql();
$this->leftJoinOwner($qb, $ownerId);
$circles = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$circles[] = $this->parseCirclesSelectSql($data, true);
}
$cursor->closeCursor();
return $circles;
}
/**
* forceGetCircleByName();
*
* returns data of a circle from its Name.
*
* WARNING: This function does not filters data regarding the current user/viewer.
* In case of interaction with users, do not use this method.
*
* @param $name
*
* @return null|DeprecatedCircle
* @throws CircleDoesNotExistException
*/
public function forceGetCircleByName($name) {
$qb = $this->getCirclesSelectSql();
$this->limitToName($qb, $name);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new CircleDoesNotExistException($this->l10n->t('Circle not found'));
}
return $this->parseCirclesSelectSql($data);
}
/**
* @param string $userId
* @param int $circleType
* @param string $name
* @param int $level
* @param bool $forceAll
* @param string $ownerId
*
* @return DeprecatedCircle[]
* @throws ConfigNoCircleAvailableException
* @throws GSStatusException
*/
public function getCircles(
string $userId, int $circleType = 0, string $name = '', int $level = 0, bool $forceAll = false,
string $ownerId = ''
) {
if ($circleType === 0) {
$circleType = DeprecatedCircle::CIRCLES_ALL;
}
// todo - make it works based on $type
$typeViewer = DeprecatedMember::TYPE_USER;
$qb = $this->getCirclesSelectSql();
$this->leftJoinUserIdAsViewer($qb, $userId, $typeViewer, '');
$this->leftJoinOwner($qb, $ownerId);
$this->leftJoinNCGroupAndUser($qb, $userId, 'c.unique_id');
if ($level > 0) {
$this->limitToLevel($qb, $level, ['u', 'g']);
}
$this->limitRegardingCircleType($qb, $userId, -1, $circleType, $name, $forceAll);
$circles = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
if ($name === '' || stripos(strtolower($data['name']), strtolower($name)) !== false
|| stripos(strtolower($data['alt_name']), strtolower($name)) !== false) {
$circles[] = $this->parseCirclesSelectSql($data);
}
}
$cursor->closeCursor();
return $circles;
}
/**
*
* @param string $circleUniqueId
* @param string $viewerId
* @param int $type
* @param string $instanceId
* @param bool $forceAll
*
* @return DeprecatedCircle
* @throws CircleDoesNotExistException
* @throws ConfigNoCircleAvailableException
*/
public function getCircle(
string $circleUniqueId, string $viewerId, int $type = DeprecatedMember::TYPE_USER,
string $instanceId = '',
bool $forceAll = false
) {
$qb = $this->getCirclesSelectSql();
$this->limitToUniqueId($qb, $circleUniqueId);
$this->leftJoinUserIdAsViewer($qb, $viewerId, $type, $instanceId);
$this->leftJoinOwner($qb);
if ($instanceId === '') {
$this->leftJoinNCGroupAndUser($qb, $viewerId, 'c.unique_id');
}
$this->limitRegardingCircleType(
$qb, $viewerId, $circleUniqueId, DeprecatedCircle::CIRCLES_ALL, '', $forceAll
);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new CircleDoesNotExistException($this->l10n->t('Circle not found ' . $circleUniqueId));
}
$circle = $this->parseCirclesSelectSql($data);
if ($instanceId === '') {
$circle->setGroupViewer(
$this->membersRequest->forceGetHigherLevelGroupFromUser($circleUniqueId, $viewerId)
);
}
return $circle;
}
/**
* createCircle();
*
* Create a circle with $userId as its owner.
* Will returns the circle
*
* @param DeprecatedCircle $circle
*/
public function createCircle(DeprecatedCircle $circle) {
$config = DeprecatedCircle::convertTypeToConfig($circle->getType());
$qb = $this->getCirclesInsertSql();
$qb->setValue('unique_id', $qb->createNamedParameter($circle->getUniqueId()))
->setValue('long_id', $qb->createNamedParameter($circle->getUniqueId(true)))
->setValue('name', $qb->createNamedParameter($circle->getName(true)))
->setValue('alt_name', $qb->createNamedParameter($circle->getAltName()))
->setValue('description', $qb->createNamedParameter($circle->getDescription()))
->setValue('contact_addressbook', $qb->createNamedParameter($circle->getContactAddressBook()))
->setValue('contact_groupname', $qb->createNamedParameter($circle->getContactGroupName()))
->setValue('settings', $qb->createNamedParameter($circle->getSettings(true)))
->setValue('type', $qb->createNamedParameter($circle->getType()))
->setValue('config', $qb->createNamedParameter($config));
$qb->execute();
}
/**
* remove a circle
*
* @param string $circleUniqueId
*/
public function destroyCircle($circleUniqueId) {
}
/**
* returns if the circle is already in database
*
* @param DeprecatedCircle $circle
* @param string $userId
*
* @return bool
* @throws ConfigNoCircleAvailableException
*/
public function isCircleUnique(DeprecatedCircle $circle, $userId = '') {
if ($circle->getType() === DeprecatedCircle::CIRCLES_PERSONAL) {
return $this->isPersonalCircleUnique($circle, $userId);
}
$qb = $this->getCirclesSelectSql();
$this->limitToNonPersonalCircle($qb);
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
if (strtolower($data['name']) === strtolower($circle->getName())
&& $circle->getUniqueId(true) !== $data['unique_id']) {
return false;
}
}
$cursor->closeCursor();
return true;
}
/**
* return if the personal circle is unique
*
* @param DeprecatedCircle $circle
* @param string $userId
*
* @return bool
* @throws ConfigNoCircleAvailableException
*/
private function isPersonalCircleUnique(DeprecatedCircle $circle, $userId = '') {
if ($userId === '') {
return true;
}
$list = $this->getCircles(
$userId, DeprecatedCircle::CIRCLES_PERSONAL, $circle->getName(),
DeprecatedMember::LEVEL_OWNER
);
foreach ($list as $test) {
if (strtolower($test->getName()) === strtolower($circle->getName())
&& $circle->getUniqueId(true) !== $test->getUniqueId(true)) {
return false;
}
}
return true;
}
/**
* @param string $uniqueId
*
* @return DeprecatedCircle
* @throws CircleDoesNotExistException
*/
public function getCircleFromUniqueId($uniqueId) {
$qb = $this->getCirclesSelectSql();
$this->limitToUniqueId($qb, (string)$uniqueId);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new CircleDoesNotExistException($this->l10n->t('Circle not found'));
}
return $this->parseCirclesSelectSql($data);
}
/**
* @param int $addressBookId
*
* @return array
*/
public function getFromBook(int $addressBookId) {
$qb = $this->getCirclesSelectSql();
$this->limitToAddressBookId($qb, $addressBookId);
$circles = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$circles[] = $this->parseCirclesSelectSql($data);
}
$cursor->closeCursor();
return $circles;
}
/**
* @param int $addressBookId
*
* @return DeprecatedCircle[]
*/
public function getFromContactBook(int $addressBookId): array {
$qb = $this->getCirclesSelectSql();
if ($addressBookId > 0) {
$this->limitToAddressBookId($qb, $addressBookId);
}
$circles = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$circles[] = $this->parseCirclesSelectSql($data);
}
$cursor->closeCursor();
return $circles;
}
/**
* @param int $addressBookId
* @param string $group
*
* @return DeprecatedCircle
* @throws CircleDoesNotExistException
*/
public function getFromContactGroup(int $addressBookId, string $group): DeprecatedCircle {
$qb = $this->getCirclesSelectSql();
$this->limitToAddressBookId($qb, $addressBookId);
$this->limitToContactGroup($qb, $group);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new CircleDoesNotExistException($this->l10n->t('Circle not found'));
}
return $this->parseCirclesSelectSql($data);
}
}
@@ -0,0 +1,270 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use Doctrine\DBAL\Query\QueryBuilder;
use OCA\Circles\Exceptions\ConfigNoCircleAvailableException;
use OCA\Circles\Model\DeprecatedCircle;
use OCA\Circles\Model\DeprecatedMember;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\MiscService;
use OCA\Circles\Service\TimezoneService;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IL10N;
class DeprecatedCirclesRequestBuilder extends DeprecatedRequestBuilder {
/** @var DeprecatedMembersRequest */
protected $membersRequest;
/**
* CirclesRequestBuilder constructor.
*
* {@inheritdoc}
* @param DeprecatedMembersRequest $membersRequest
*/
public function __construct(
IL10N $l10n, IDBConnection $connection, DeprecatedMembersRequest $membersRequest,
ConfigService $configService, TimezoneService $timezoneService, MiscService $miscService
) {
parent::__construct($l10n, $connection, $configService, $timezoneService, $miscService);
$this->membersRequest = $membersRequest;
}
/**
* Limit the search to a non-personal circle
*
* @param IQueryBuilder $qb
*/
protected function limitToNonPersonalCircle(IQueryBuilder $qb) {
$expr = $qb->expr();
$qb->andWhere(
$expr->neq('c.type', $qb->createNamedParameter(DeprecatedCircle::CIRCLES_PERSONAL))
);
}
/**
* @param IQueryBuilder $qb
* @param string $userId
* @param string $circleUniqueId
* @param $type
* @param $name
* @param bool $forceAll
*
* @deprecated
* @throws ConfigNoCircleAvailableException
*/
protected function limitRegardingCircleType(
IQueryBuilder $qb, string $userId, $circleUniqueId, int $type,
string $name, bool $forceAll = false
) {
}
/**
* @param IQueryBuilder $qb
* @param string $circleUniqueId
* @param $userId
* @param $type
* @param $name
* @param bool $forceAll
*
* @return array
*/
private function generateLimit(
IQueryBuilder $qb, $circleUniqueId, $userId, $type, $name, $forceAll = false
) {
return [];
}
/**
* add a request to the members list, using the current user ID.
* will returns level and stuff.
*
* @param IQueryBuilder $qb
* @param string $userId
* @param int $type
* @param string $instanceId
*/
public function leftJoinUserIdAsViewer(IQueryBuilder $qb, string $userId, int $type, string $instanceId
) {
if ($qb->getType() !== QueryBuilder::SELECT) {
return;
}
$expr = $qb->expr();
$pf = '' . $this->default_select_alias . '.';
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->selectAlias('u.user_id', 'viewer_userid')
->selectAlias('u.user_type', 'viewer_type')
->selectAlias('u.instance', 'viewer_instance')
->selectAlias('u.status', 'viewer_status')
->selectAlias('u.member_id', 'viewer_member_id')
->selectAlias('u.cached_name', 'viewer_cached_name')
->selectAlias('u.cached_update', 'viewer_cached_update')
->selectAlias('u.level', 'viewer_level')
->leftJoin(
$this->default_select_alias, DeprecatedRequestBuilder::TABLE_MEMBERS, 'u',
$expr->andX(
$expr->eq('u.circle_id', $pf . 'unique_id'),
$expr->eq('u.user_id', $qb->createNamedParameter($userId)),
$expr->eq('u.instance', $qb->createNamedParameter($instanceId)),
$expr->eq('u.user_type', $qb->createNamedParameter($type))
)
);
}
/**
* Left Join members table to get the owner of the circle.
*
* @param IQueryBuilder $qb
* @param string $ownerId
*/
public function leftJoinOwner(IQueryBuilder $qb, string $ownerId = '') {
if ($qb->getType() !== QueryBuilder::SELECT) {
return;
}
$expr = $qb->expr();
$pf = $this->default_select_alias . '.';
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->selectAlias('o.user_id', 'owner_userid')
->selectAlias('o.member_id', 'owner_member_id')
->selectAlias('o.instance', 'owner_instance')
->selectAlias('o.cached_name', 'owner_cached_name')
->selectAlias('o.cached_update', 'owner_cached_update')
->selectAlias('o.status', 'owner_status')
->selectAlias('o.level', 'owner_level')
->leftJoin(
$this->default_select_alias, DeprecatedRequestBuilder::TABLE_MEMBERS, 'o',
$expr->andX(
$expr->eq('o.circle_id', $pf . 'unique_id'),
$expr->eq('o.level', $qb->createNamedParameter(DeprecatedMember::LEVEL_OWNER)),
$expr->eq('o.user_type', $qb->createNamedParameter(DeprecatedMember::TYPE_USER))
)
);
if ($ownerId !== '') {
$qb->andWhere($expr->eq('o.user_id', $qb->createNamedParameter($ownerId)));
}
}
/**
* Base of the Sql Insert request for Shares
*
*
* @return IQueryBuilder
*/
protected function getCirclesInsertSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::TABLE_CIRCLES)
->setValue('creation', $qb->createNamedParameter($this->timezoneService->getUTCDate()));
return $qb;
}
/**
* @return IQueryBuilder
*/
protected function getCirclesSelectSql() {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->selectDistinct('c.unique_id')
->addSelect(
'c.id', 'c.name', 'c.alt_name', 'c.description', 'c.settings', 'c.type', 'contact_addressbook',
'contact_groupname', 'c.creation'
)
->from(DeprecatedRequestBuilder::TABLE_CIRCLES, 'c');
$this->default_select_alias = 'c';
return $qb;
}
/**
* @param array $data
* @param bool $allSettings
*
* @return DeprecatedCircle
*/
protected function parseCirclesSelectSql($data, bool $allSettings = false) {
$circle = new DeprecatedCircle();
$circle->setId($data['id']);
$circle->setUniqueId($data['unique_id']);
$circle->setName($data['name']);
$circle->setAltName($data['alt_name']);
$circle->setDescription($data['description']);
if ($data['contact_addressbook'] !== null) {
$circle->setContactAddressBook($data['contact_addressbook']);
}
if ($data['contact_groupname'] !== null) {
$circle->setContactGroupName($data['contact_groupname']);
}
$circle->setSettings($data['settings'], $allSettings);
$circle->setType($data['type']);
$circle->setCreation($data['creation']);
if (key_exists('viewer_level', $data)) {
$user = new DeprecatedMember(
$data['viewer_userid'], DeprecatedMember::TYPE_USER, $circle->getUniqueId()
);
$user->setStatus($data['viewer_status']);
$user->setMemberId($data['viewer_member_id']);
$user->setCachedName($data['viewer_cached_name']);
$user->setType($data['viewer_type']);
$user->setInstance($data['viewer_instance']);
$user->setLevel($data['viewer_level']);
$circle->setViewer($user);
}
if (key_exists('owner_level', $data)) {
$owner = new DeprecatedMember(
$data['owner_userid'], DeprecatedMember::TYPE_USER, $circle->getUniqueId()
);
$owner->setCachedName($data['owner_cached_name']);
$owner->setMemberId($data['owner_member_id']);
$owner->setStatus($data['owner_status']);
$owner->setInstance($data['owner_instance']);
$owner->setLevel($data['owner_level']);
$circle->setOwner($owner);
}
return $circle;
}
}
@@ -0,0 +1,781 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Traits\TStringTools;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Exception;
use OCA\Circles\Exceptions\GSStatusException;
use OCA\Circles\Exceptions\MemberAlreadyExistsException;
use OCA\Circles\Exceptions\MemberDoesNotExistException;
use OCA\Circles\Model\DeprecatedMember;
use OCP\IGroup;
class DeprecatedMembersRequest extends DeprecatedMembersRequestBuilder {
use TStringTools;
/**
* Returns information about a member.
*
* WARNING: This function does not filters data regarding the current user/viewer.
* In case of interaction with users, Please use MembersService->getMember() instead.
*
* @param string $circleUniqueId
* @param string $userId
* @param $type
*
* @param string $instance
*
* @return DeprecatedMember
* @throws MemberDoesNotExistException
*/
public function forceGetMember($circleUniqueId, $userId, $type, string $instance = '') {
$qb = $this->getMembersSelectSql();
if ($this->configService->isLocalInstance($instance)) {
$instance = '';
}
$this->limitToUserId($qb, $userId);
$this->limitToUserType($qb, $type);
$this->limitToInstance($qb, $instance);
$this->limitToCircleId($qb, $circleUniqueId);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new MemberDoesNotExistException($this->l10n->t('This member does not exist'));
}
return $this->parseMembersSelectSql($data);
}
/**
* @param string $memberId
*
* @return DeprecatedMember
* @throws MemberDoesNotExistException
*/
public function forceGetMemberById(string $memberId): DeprecatedMember {
$qb = $this->getMembersSelectSql();
$this->limitToMemberId($qb, $memberId);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new MemberDoesNotExistException($this->l10n->t('This member does not exist'));
}
return $this->parseMembersSelectSql($data);
}
/**
* Returns members list of a circle, based on their level.
*
* WARNING: This function does not filters data regarding the current user/viewer.
* In case of interaction with users, Please use getMembers() instead.
*
* @param string $circleUniqueId
* @param int $level
* @param int $type
* @param bool $incGroup
*
* @return DeprecatedMember[]
*/
public function forceGetMembers(
string $circleUniqueId, $level = DeprecatedMember::LEVEL_MEMBER, int $type = 0, $incGroup = false
) {
$qb = $this->getMembersSelectSql();
$this->limitToMembersAndAlmost($qb);
$this->limitToLevel($qb, $level);
if ($type > 0) {
$this->limitToUserType($qb, $type);
}
$this->limitToCircleId($qb, $circleUniqueId);
$members = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$members[] = $this->parseMembersSelectSql($data);
}
$cursor->closeCursor();
try {
// if ($this->configService->isLinkedGroupsAllowed() && $incGroup === true) {
// $this->includeGroupMembers($members, $circleUniqueId, $level);
// }
} catch (GSStatusException $e) {
}
return $members;
}
/**
* Returns all members.
*
* WARNING: This function does not filters data regarding the current user/viewer.
* In case of interaction with users, Please use getMembers() instead.
*
*
* @return DeprecatedMember[]
*/
public function forceGetAllMembers() {
$qb = $this->getMembersSelectSql();
$members = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$members[] = $this->parseMembersSelectSql($data);
}
$cursor->closeCursor();
return $members;
}
/**
* Returns members generated from Contacts that are not 'checked' (as not sent existing shares).
*
*
* @return DeprecatedMember[]
*/
public function forceGetAllRecentContactEdit() {
$qb = $this->getMembersSelectSql();
$this->limitToUserType($qb, DeprecatedMember::TYPE_CONTACT);
$expr = $qb->expr();
$orX = $expr->orX();
$orX->add($expr->isNull('contact_checked'));
$orX->add($expr->neq('contact_checked', $qb->createNamedParameter('1')));
$qb->andWhere($orX);
$members = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$members[] = $this->parseMembersSelectSql($data);
}
$cursor->closeCursor();
return $members;
}
/**
* @param DeprecatedMember $member
* @param bool $check
*/
public function checkMember(DeprecatedMember $member, bool $check) {
$qb = $this->getMembersUpdateSql(
$member->getCircleId(), $member->getUserId(), $member->getInstance(), $member->getType()
);
$qb->set('contact_checked', $qb->createNamedParameter(($check) ? 1 : 0));
$qb->execute();
}
/**
* @param string $circleUniqueId
* @param DeprecatedMember $viewer
* @param bool $force
*
* @return DeprecatedMember[]
*/
public function getMembers(string $circleUniqueId, ?DeprecatedMember $viewer, bool $force = false) {
try {
if ($force === false) {
$viewer->hasToBeMember();
}
$members = $this->forceGetMembers($circleUniqueId, DeprecatedMember::LEVEL_NONE);
if ($force === false) {
if (!$viewer->isLevel(DeprecatedMember::LEVEL_MODERATOR)) {
array_map(
function (DeprecatedMember $m) {
$m->setNote('');
}, $members
);
}
}
return $members;
} catch (Exception $e) {
return [];
}
}
/**
* forceGetGroup();
*
* returns group information as a member within a Circle.
*
* WARNING: This function does not filters data regarding the current user/viewer.
* In case of interaction with users, Please use getGroup() instead.
*
* @param string $circleUniqueId
* @param string $groupId
* @param string $instance
*
* @return DeprecatedMember
* @throws MemberDoesNotExistException
*/
public function forceGetGroup(string $circleUniqueId, string $groupId, string $instance) {
$qb = $this->getMembersSelectSql();
$this->limitToUserId($qb, $groupId);
$this->limitToUserType($qb, DeprecatedMember::TYPE_GROUP);
$this->limitToInstance($qb, $instance);
$this->limitToCircleId($qb, $circleUniqueId);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new MemberDoesNotExistException($this->l10n->t('This member does not exist'));
}
return $this->parseMembersSelectSql($data);
}
/**
* includeGroupMembers();
*
* This function will get members of a circle throw NCGroups and fill the result an existing
* Members List. In case of duplicate, higher level will be kept.
*
* @param DeprecatedMember[] $members
* @param string $circleUniqueId
* @param int $level
*/
private function includeGroupMembers(array &$members, $circleUniqueId, $level) {
$groupMembers = $this->forceGetGroupMembers($circleUniqueId, $level);
$this->avoidDuplicateMembers($members, $groupMembers);
}
/**
* avoidDuplicateMembers();
*
* Use this function to add members to the list (1st argument), keeping the higher level in case
* of duplicate
*
* @param DeprecatedMember[] $members
* @param DeprecatedMember[] $groupMembers
*/
public function avoidDuplicateMembers(array &$members, array $groupMembers) {
foreach ($groupMembers as $member) {
$index = $this->indexOfMember($members, $member->getUserId());
if ($index === -1) {
array_push($members, $member);
} elseif ($members[$index]->getLevel() < $member->getLevel()) {
$members[$index] = $member;
}
}
}
/**
* returns the index of a specific UserID in a Members List
*
* @param array $members
* @param $userId
*
* @return int
*/
private function indexOfMember(array $members, $userId) {
foreach ($members as $k => $member) {
if ($member->getUserId() === $userId) {
return intval($k);
}
}
return -1;
}
/**
* Check if a fresh member can be generated (by addMember/joinCircle)
*
* @param string $circleUniqueId
* @param string $name
* @param int $type
*
* @param string $instance
*
* @return DeprecatedMember
*/
public function getFreshNewMember($circleUniqueId, string $name, int $type, string $instance) {
try {
$member = $this->forceGetMember($circleUniqueId, $name, $type, $instance);
} catch (MemberDoesNotExistException $e) {
$member = new DeprecatedMember($name, $type, $circleUniqueId);
$member->setInstance($instance);
// $member->setMemberId($this->token(14));
}
// if ($member->alreadyExistOrJoining()) {
// throw new MemberAlreadyExistsException(
// $this->l10n->t('This account is already a member of the circle')
// );
// }
return $member;
}
/**
* Returns members list of all Group Members of a Circle. The Level of the linked group will be
* assigned to each entry
*
* NOTE: Can contains duplicate.
*
* WARNING: This function does not filters data regarding the current user/viewer.
* Do not use in case of direct interaction with users.
*
* @param string $circleUniqueId
* @param int $level
*
* @return DeprecatedMember[]
*/
public function forceGetGroupMembers($circleUniqueId, $level = DeprecatedMember::LEVEL_MEMBER) {
$qb = $this->getMembersSelectSql();
$this->limitToUserType($qb, DeprecatedMember::TYPE_GROUP);
$this->limitToLevel($qb, $level);
$this->limitToCircleId($qb, $circleUniqueId);
$this->limitToNCGroupUser($qb);
$members = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$members[] = $this->parseGroupsSelectSql($data);
}
$cursor->closeCursor();
return $members;
}
/**
* returns all users from a Group as a list of Members.
*
* @param DeprecatedMember $group
*
* @return DeprecatedMember[]
*/
public function getGroupMemberMembers(DeprecatedMember $group) {
/** @var IGroup $grp */
$grp = $this->groupManager->get($group->getUserId());
if ($grp === null) {
return [];
}
$members = [];
$users = $grp->getUsers();
foreach ($users as $user) {
$member = clone $group;
//Member::fromJSON($this->l10n, json_encode($group));
$member->setType(DeprecatedMember::TYPE_USER);
$member->setUserId($user->getUID());
$members[] = $member;
}
return $members;
}
/**
* return the higher level group linked to a circle, that include the userId.
*
* WARNING: This function does not filters data regarding the current user/viewer.
* In case of direct interaction with users, Please don't use this.
*
* @param string $circleUniqueId
* @param string $userId
*
* @return DeprecatedMember
*/
public function forceGetHigherLevelGroupFromUser($circleUniqueId, $userId) {
$qb = $this->getMembersSelectSql();
$this->limitToUserType($qb, DeprecatedMember::TYPE_GROUP);
$this->limitToInstance($qb, '');
$this->limitToCircleId($qb, $circleUniqueId);
$this->limitToNCGroupUser($qb);
$this->limitToNCGroupUser($qb, $userId);
/** @var DeprecatedMember $group */
$group = null;
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$entry = $this->parseGroupsSelectSql($data);
if ($group === null || $entry->getLevel() > $group->getLevel()) {
$group = $entry;
}
}
$cursor->closeCursor();
return $group;
}
/**
* Insert Member into database.
*
* @param DeprecatedMember $member
*
* @throws MemberAlreadyExistsException
*/
public function createMember(DeprecatedMember $member) {
if ($member->getMemberId() === '') {
$member->setMemberId($this->token(14));
}
$instance = $member->getInstance();
if ($this->configService->isLocalInstance($instance)) {
$instance = '';
}
try {
$qb = $this->getMembersInsertSql();
$qb->setValue('circle_id', $qb->createNamedParameter($member->getCircleId()))
->setValue('user_id', $qb->createNamedParameter($member->getUserId()))
->setValue('member_id', $qb->createNamedParameter($member->getMemberId()))
->setValue('user_type', $qb->createNamedParameter($member->getType()))
->setValue('cached_name', $qb->createNamedParameter($member->getCachedName()))
->setValue('cached_update', $qb->createNamedParameter($this->timezoneService->getUTCDate()))
->setValue('instance', $qb->createNamedParameter($instance))
->setValue('level', $qb->createNamedParameter($member->getLevel()))
->setValue('status', $qb->createNamedParameter($member->getStatus()))
->setValue('contact_id', $qb->createNamedParameter($member->getContactId()))
->setValue('note', $qb->createNamedParameter($member->getNote()));
$qb->execute();
} catch (UniqueConstraintViolationException $e) {
throw new MemberAlreadyExistsException(
$this->l10n->t('This account is already a member of the circle')
);
}
}
/**
* @param string $circleUniqueId
* @param DeprecatedMember $viewer
*
* @return DeprecatedMember[]
*/
public function getGroupsFromCircle($circleUniqueId, DeprecatedMember $viewer) {
if ($viewer->getLevel() < DeprecatedMember::LEVEL_MEMBER) {
return [];
}
$qb = $this->getMembersSelectSql();
$this->limitToUserType($qb, DeprecatedMember::TYPE_GROUP);
$this->limitToLevel($qb, DeprecatedMember::LEVEL_MEMBER);
$this->limitToInstance($qb, '');
$this->limitToCircleId($qb, $circleUniqueId);
$cursor = $qb->execute();
$groups = [];
while ($data = $cursor->fetch()) {
if ($viewer->getLevel() < DeprecatedMember::LEVEL_MODERATOR) {
$data['note'] = '';
}
$groups[] = $this->parseGroupsSelectSql($data);
}
$cursor->closeCursor();
return $groups;
}
/**
* update database entry for a specific Member.
*
* @param DeprecatedMember $member
*/
public function updateMemberLevel(DeprecatedMember $member) {
$instance = $member->getInstance();
if ($this->configService->isLocalInstance($instance)) {
$instance = '';
}
$qb = $this->getMembersUpdateSql(
$member->getCircleId(), $member->getUserId(), $instance, $member->getType()
);
$qb->set('level', $qb->createNamedParameter($member->getLevel()))
->set('status', $qb->createNamedParameter($member->getStatus()));
$qb->execute();
}
/**
* update database entry for a specific Member.
*
* @param DeprecatedMember $member
*/
public function updateMemberInfo(DeprecatedMember $member) {
$instance = $member->getInstance();
if ($this->configService->isLocalInstance($instance)) {
$instance = '';
}
$qb = $this->getMembersUpdateSql(
$member->getCircleId(), $member->getUserId(), $instance, $member->getType()
);
$qb->set('note', $qb->createNamedParameter($member->getNote()))
->set('cached_name', $qb->createNamedParameter($member->getCachedName()))
->set('cached_update', $qb->createNamedParameter($this->timezoneService->getUTCDate()));
$qb->execute();
}
/**
* update database entry for a specific Member.
*
* @param DeprecatedMember $member
*/
public function updateContactMeta(DeprecatedMember $member) {
$qb = $this->getMembersUpdateSql(
$member->getCircleId(), $member->getUserId(), $member->getInstance(), $member->getType()
);
$qb->set('contact_meta', $qb->createNamedParameter(json_encode($member->getContactMeta())));
$qb->execute();
}
/**
* removeAllFromCircle();
*
* Remove All members from a Circle. Used when deleting a Circle.
*
* @param string $uniqueCircleId
*/
public function removeAllFromCircle($uniqueCircleId) {
$qb = $this->getMembersDeleteSql();
$expr = $qb->expr();
$qb->where($expr->eq('circle_id', $qb->createNamedParameter($uniqueCircleId)));
$qb->execute();
}
/**
* removeAllMembershipsFromUser();
*
* remove All membership from a User. Used when removing a User from the Cloud.
*
* @param DeprecatedMember $member
*/
public function removeAllMembershipsFromUser(DeprecatedMember $member) {
if ($member->getUserId() === '') {
return;
}
$instance = $member->getInstance();
if ($this->configService->isLocalInstance($instance)) {
$instance = '';
}
$qb = $this->getMembersDeleteSql();
$expr = $qb->expr();
$qb->where(
$expr->andX(
$expr->eq('user_id', $qb->createNamedParameter($member->getUserId())),
$expr->eq('instance', $qb->createNamedParameter($instance)),
$expr->eq('user_type', $qb->createNamedParameter(DeprecatedMember::TYPE_USER))
)
);
$qb->execute();
}
/**
* remove member, identified by its id, type and circleId
*
* @param DeprecatedMember $member
*/
public function removeMember(DeprecatedMember $member) {
$instance = $member->getInstance();
if ($this->configService->isLocalInstance($instance)) {
$instance = '';
}
$qb = $this->getMembersDeleteSql();
$this->limitToCircleId($qb, $member->getCircleId());
$this->limitToUserId($qb, $member->getUserId());
$this->limitToInstance($qb, $instance);
$this->limitToUserType($qb, $member->getType());
if ($member->getContactId() !== '') {
$this->limitToContactId($qb, $member->getContactId());
}
$qb->execute();
}
/**
* update database entry for a specific Group.
*
* @param DeprecatedMember $member
*
* @return bool
*/
public function updateGroup(DeprecatedMember $member) {
$qb = $this->getMembersUpdateSql(
$member->getCircleId(), $member->getUserId(), $member->getInstance(), $member->getType()
);
$qb->set('level', $qb->createNamedParameter($member->getLevel()));
$qb->execute();
return true;
}
public function unlinkAllFromGroup($groupId) {
$qb = $this->getMembersDeleteSql();
$this->limitToUserId($qb, $groupId);
$this->limitToUserType($qb, DeprecatedMember::TYPE_GROUP);
$this->limitToInstance($qb, '');
$qb->execute();
}
/**
* @param string $contactId
*
* @return DeprecatedMember[]
*/
public function getMembersByContactId(string $contactId = ''): array {
$qb = $this->getMembersSelectSql();
if ($contactId === '') {
$expr = $qb->expr();
$qb->andWhere($expr->neq('contact_id', $qb->createNamedParameter('')));
} else {
$this->limitToContactId($qb, $contactId);
}
$members = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$member = $this->parseMembersSelectSql($data);
$members[] = $member;
}
$cursor->closeCursor();
return $members;
}
/**
* @param string $circleId
* @param string $contactId
*
* @return DeprecatedMember
* @throws MemberDoesNotExistException
*/
public function getContactMember(string $circleId, string $contactId): DeprecatedMember {
$qb = $this->getMembersSelectSql();
$this->limitToContactId($qb, $contactId);
$this->limitToCircleId($qb, $circleId);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new MemberDoesNotExistException($this->l10n->t('This member does not exist'));
}
return $this->parseMembersSelectSql($data);
}
/**
* @param string $contactId
*
* @return DeprecatedMember[]
*/
public function getLocalContactMembers(string $contactId): array {
$qb = $this->getMembersSelectSql();
$this->limitToContactId($qb, $contactId);
$this->limitToUserType($qb, DeprecatedMember::TYPE_USER);
$members = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$members[] = $this->parseMembersSelectSql($data);
}
$cursor->closeCursor();
return $members;
}
/**
* @param string $contactId
* @param int $type
*/
public function removeMembersByContactId(string $contactId, int $type = 0) {
$this->miscService->log($contactId);
if ($contactId === '') {
return;
}
$qb = $this->getMembersDeleteSql();
$this->limitToContactId($qb, $contactId);
if ($type > 0) {
$this->limitToUserType($qb, $type);
}
$qb->execute();
}
}
@@ -0,0 +1,188 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Model\DeprecatedMember;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\MiscService;
use OCA\Circles\Service\TimezoneService;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IL10N;
class DeprecatedMembersRequestBuilder extends DeprecatedRequestBuilder {
/** @var IGroupManager */
protected $groupManager;
/**
* CirclesRequestBuilder constructor.
*
* {@inheritdoc}
* @param IGroupManager $groupManager
*/
public function __construct(
IL10N $l10n, IDBConnection $connection, IGroupManager $groupManager,
ConfigService $configService, TimezoneService $timezoneService, MiscService $miscService
) {
parent::__construct($l10n, $connection, $configService, $timezoneService, $miscService);
$this->groupManager = $groupManager;
}
/**
* Base of the Sql Insert request for Shares
*
* @return IQueryBuilder
*/
protected function getMembersInsertSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::TABLE_MEMBERS)
->setValue('joined', $qb->createNamedParameter($this->timezoneService->getUTCDate()));
return $qb;
}
/**
* @return IQueryBuilder
*/
protected function getMembersSelectSql() {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select(
'm.user_id', 'm.instance', 'm.user_type', 'm.circle_id', 'm.level', 'm.status', 'm.note',
'm.contact_id', 'm.member_id', 'm.cached_name', 'm.cached_update', 'm.contact_meta', 'm.joined'
)
->from(self::TABLE_MEMBERS, 'm')
->orderBy('m.joined');
$this->default_select_alias = 'm';
return $qb;
}
/**
* Base of the Sql Updte request for Members
*
* @param string /$circleId
* @param string $userId
* @param string $instance
* @param int $type
*
* @return IQueryBuilder
*/
protected function getMembersUpdateSql(string $circleId, string $userId, string $instance, int $type) {
$qb = $this->dbConnection->getQueryBuilder();
$expr = $qb->expr();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->update(self::TABLE_MEMBERS)
->where(
$expr->andX(
$expr->eq('circle_id', $qb->createNamedParameter($circleId)),
$expr->eq('user_id', $qb->createNamedParameter($userId)),
$expr->eq('instance', $qb->createNamedParameter($instance)),
$expr->eq('user_type', $qb->createNamedParameter($type))
)
);
return $qb;
}
/**
* Base of the Sql Delete request for Members
*
* @return IQueryBuilder
*/
protected function getMembersDeleteSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(DeprecatedRequestBuilder::TABLE_MEMBERS);
return $qb;
}
/**
* @param array $data
*
* @return DeprecatedMember
*/
protected function parseMembersSelectSql(array $data) {
$member = new DeprecatedMember($data['user_id'], $data['user_type'], $data['circle_id']);
$member->setNote($data['note']);
$member->setContactId($data['contact_id']);
$member->setMemberId($data['member_id']);
$member->setCachedName($data['cached_name']);
$member->setCachedUpdate($this->timezoneService->convertToTimestamp($data['cached_update']));
$contactMeta = json_decode($data['contact_meta'], true);
if (is_array($contactMeta)) {
$member->setContactMeta($contactMeta);
}
$member->setLevel($data['level']);
$member->setInstance($data['instance']);
$member->setStatus($data['status']);
$member->setJoined($this->timezoneService->convertTimeForCurrentUser($data['joined']));
$joined = $this->timezoneService->convertToTimestamp($data['joined']);
$member->setJoinedSince(time() - $joined);
return $member;
}
/**
* @param array $data
*
* @return DeprecatedMember
*/
protected function parseGroupsSelectSql(array $data) {
$member = new DeprecatedMember();
$member->setCircleId($data['circle_id']);
$member->setNote($data['note']);
$member->setLevel($data['level']);
if (key_exists('user_id', $data)) {
$member->setType(DeprecatedMember::TYPE_USER);
$member->setUserId($data['user_id']);
} else {
$member->setType(DeprecatedMember::TYPE_GROUP);
$member->setUserId($data['group_id']);
}
$member->setJoined($this->timezoneService->convertTimeForCurrentUser($data['joined']));
return $member;
}
}
@@ -0,0 +1,513 @@
<?php
/**
* Created by PhpStorm.
* User: maxence
* Date: 7/4/17
* Time: 5:01 PM
*/
namespace OCA\Circles\Db;
use Doctrine\DBAL\Query\QueryBuilder;
use OCA\Circles\Exceptions\GSStatusException;
use OCA\Circles\Model\DeprecatedMember;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\MiscService;
use OCA\Circles\Service\TimezoneService;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IL10N;
class DeprecatedRequestBuilder {
public const TABLE_FILE_SHARES = 'share';
public const SHARE_TYPE = 7;
public const TABLE_CIRCLES = 'circle_circles';
public const TABLE_MEMBERS = 'circle_members';
public const TABLE_GROUPS = 'circle_groups';
public const TABLE_SHARES = 'circle_shares';
public const TABLE_LINKS = 'circle_links';
public const TABLE_TOKENS = 'circle_tokens';
public const TABLE_GSEVENTS = 'circle_gsevents';
public const TABLE_GSSHARES = 'circle_gsshares';
public const TABLE_GSSHARES_MOUNTPOINT = 'circle_gsshares_mp';
public const TABLE_REMOTE = 'circle_remotes';
public const NC_TABLE_ACCOUNTS = 'accounts';
public const NC_TABLE_GROUP_USER = 'group_user';
/** @var array */
private $tables = [
self::TABLE_CIRCLES,
self::TABLE_GROUPS,
self::TABLE_MEMBERS,
self::TABLE_SHARES,
self::TABLE_LINKS,
self::TABLE_TOKENS,
self::TABLE_GSEVENTS,
self::TABLE_GSSHARES,
self::TABLE_GSSHARES_MOUNTPOINT
];
/** @var IDBConnection */
protected $dbConnection;
/** @var IL10N */
protected $l10n;
/** @var ConfigService */
protected $configService;
/** @var TimezoneService */
protected $timezoneService;
/** @var MiscService */
protected $miscService;
/** @var string */
protected $default_select_alias;
/** @var bool */
protected $leftJoinedNCGroupAndUser = false;
/**
* CoreQueryBuilder constructor.
*
* @param IL10N $l10n
* @param IDBConnection $connection
* @param ConfigService $configService
* @param TimezoneService $timezoneService
* @param MiscService $miscService
*/
public function __construct(
IL10N $l10n, IDBConnection $connection, ConfigService $configService,
TimezoneService $timezoneService, MiscService $miscService
) {
$this->l10n = $l10n;
$this->dbConnection = $connection;
$this->configService = $configService;
$this->timezoneService = $timezoneService;
$this->miscService = $miscService;
}
/**
* Limit the request by its Id.
*
* @param IQueryBuilder $qb
* @param int $id
*/
protected function limitToId(IQueryBuilder $qb, $id) {
$this->limitToDBField($qb, 'id', $id);
}
/**
* Limit the request by its UniqueId.
*
* @param IQueryBuilder $qb
* @param int $uniqueId
*/
protected function limitToUniqueId(IQueryBuilder $qb, $uniqueId) {
$this->limitToDBField($qb, 'unique_id', $uniqueId);
}
/**
* Limit the request by its addressbookId.
*
* @param IQueryBuilder $qb
* @param int $bookId
*/
protected function limitToAddressBookId(IQueryBuilder $qb, $bookId) {
$this->limitToDBField($qb, 'contact_addressbook', (string)$bookId);
}
/**
* Limit the request by its addressbookId.
*
* @param IQueryBuilder $qb
* @param string $groupName
*/
protected function limitToContactGroup(IQueryBuilder $qb, $groupName) {
$this->limitToDBField($qb, 'contact_groupname', $groupName);
}
/**
* Limit the request to the Circle by its Id.
*
* @param IQueryBuilder $qb
* @param string $contactId
*/
protected function limitToContactId(IQueryBuilder $qb, $contactId) {
$this->limitToDBField($qb, 'contact_id', $contactId);
}
/**
* Limit the request by its Token.
*
* @param IQueryBuilder $qb
* @param string $token
*/
protected function limitToToken(IQueryBuilder $qb, $token) {
$this->limitToDBField($qb, 'token', $token);
}
/**
* Limit the request to the User by its Id.
*
* @param IQueryBuilder $qb
* @param $userId
*/
protected function limitToUserId(IQueryBuilder $qb, $userId) {
$this->limitToDBField($qb, 'user_id', $userId);
}
/**
* Limit the request to the owner
*
* @param IQueryBuilder $qb
* @param $owner
*/
protected function limitToOwner(IQueryBuilder $qb, $owner) {
$this->limitToDBField($qb, 'owner', $owner);
}
/**
* Limit the request to the Member by its Id.
*
* @param IQueryBuilder $qb
* @param string $memberId
*/
protected function limitToMemberId(IQueryBuilder $qb, string $memberId) {
$this->limitToDBField($qb, 'member_id', $memberId);
}
/**
* Limit the request to the Type entry.
*
* @param IQueryBuilder $qb
* @param int $type
*/
protected function limitToUserType(IQueryBuilder $qb, $type) {
$this->limitToDBField($qb, 'user_type', $type);
}
/**
* Limit the request to the Instance.
*
* @param IQueryBuilder $qb
* @param string $instance
*/
protected function limitToInstance(IQueryBuilder $qb, string $instance) {
$this->limitToDBField($qb, 'instance', $instance);
}
/**
* Limit the request to the Circle by its Id.
*
* @param IQueryBuilder $qb
* @param string $circleUniqueId
*/
protected function limitToCircleId(IQueryBuilder $qb, $circleUniqueId) {
$this->limitToDBField($qb, 'circle_id', $circleUniqueId);
}
/**
* Limit the request to the Circle by its Id.
*
* @param IQueryBuilder $qb
* @param int $shareId
*/
protected function limitToShareId(IQueryBuilder $qb, int $shareId) {
$this->limitToDBField($qb, 'share_id', $shareId);
}
/**
* Limit the request to the Circle by its Id.
*
* @param IQueryBuilder $qb
* @param string $mountpoint
*/
protected function limitToMountpoint(IQueryBuilder $qb, string $mountpoint) {
$this->limitToDBField($qb, 'share_id', $mountpoint);
}
/**
* Limit the request to the Circle by its Id.
*
* @param IQueryBuilder $qb
* @param string $hash
*/
protected function limitToMountpointHash(IQueryBuilder $qb, string $hash) {
$this->limitToDBField($qb, 'share_id', $hash);
}
//
// /**
// * Limit the request to the Circle by its Shorten Unique Id.
// *
// * @param IQueryBuilder $qb
// * @param string $circleUniqueId
// * @param $length
// */
// protected function limitToShortenUniqueId(IQueryBuilder $qb, $circleUniqueId, $length) {
// $expr = $qb->expr();
// $pf = ($qb->getType() === QueryBuilder::SELECT) ? '`' . $this->default_select_alias . '`.' : '';
//
// $qb->andWhere(
// $expr->eq(
// $qb->createNamedParameter($circleUniqueId),
// $qb->createFunction('SUBSTR(' . $pf . '`unique_id`' . ', 1, ' . $length . ')')
// )
// );
//
// }
/**
* Limit the request to the Group by its Id.
*
* @param IQueryBuilder $qb
* @param int $groupId
*/
protected function limitToGroupId(IQueryBuilder $qb, $groupId) {
$this->limitToDBField($qb, 'group_id', $groupId);
}
/**
* Limit the search by its Name
*
* @param IQueryBuilder $qb
* @param string $name
*/
protected function limitToName(IQueryBuilder $qb, $name) {
$this->limitToDBField($qb, 'name', $name);
}
/**
* Limit the search by its Status (or greater)
*
* @param IQueryBuilder $qb
* @param string $name
*/
protected function limitToStatus(IQueryBuilder $qb, $name) {
$this->limitToDBFieldOrGreater($qb, 'status', $name);
}
/**
* Limit the request by its Id.
*
* @param IQueryBuilder $qb
* @param string $type
*/
protected function limitToShareType(IQueryBuilder $qb, string $type) {
$this->limitToDBField($qb, 'share_type', $type);
}
/**
* Limit the request by its Id.
*
* @param IQueryBuilder $qb
* @param string $with
*/
protected function limitToShareWith(IQueryBuilder $qb, string $with) {
$this->limitToDBField($qb, 'share_with', $with);
}
/**
* Limit the request to a minimum member level.
*
* if $pf is an array, will generate an SQL OR request to limit level in multiple tables
*
* @param IQueryBuilder $qb
* @param int $level
* @param string|array $pf
*/
protected function limitToLevel(IQueryBuilder $qb, int $level, $pf = '') {
$expr = $qb->expr();
if ($pf === '') {
$p = ($qb->getType() === QueryBuilder::SELECT) ? $this->default_select_alias . '.' : '';
$qb->andWhere($expr->gte($p . 'level', $qb->createNamedParameter($level)));
return;
}
if (!is_array($pf)) {
$pf = [$pf];
}
$orX = $this->generateLimitToLevelMultipleTableRequest($qb, $level, $pf);
$qb->andWhere($orX);
}
/**
* @param IQueryBuilder $qb
* @param int $level
* @param array $pf
*
* @return mixed
*/
private function generateLimitToLevelMultipleTableRequest(IQueryBuilder $qb, int $level, $pf) {
$expr = $qb->expr();
$orX = $expr->orX();
foreach ($pf as $p) {
if ($p === 'g' && !$this->leftJoinedNCGroupAndUser) {
continue;
}
$orX->add($expr->gte($p . '.level', $qb->createNamedParameter($level)));
}
return $orX;
}
/**
* Limit the search to Members and Almost members
*
* @param IQueryBuilder $qb
*/
protected function limitToMembersAndAlmost(IQueryBuilder $qb) {
$expr = $qb->expr();
$pf = ($qb->getType() === QueryBuilder::SELECT) ? $this->default_select_alias . '.' : '';
$orX = $expr->orX();
$orX->add($expr->eq($pf . 'status', $qb->createNamedParameter(DeprecatedMember::STATUS_MEMBER)));
$orX->add($expr->eq($pf . 'status', $qb->createNamedParameter(DeprecatedMember::STATUS_INVITED)));
$orX->add($expr->eq($pf . 'status', $qb->createNamedParameter(DeprecatedMember::STATUS_REQUEST)));
$qb->andWhere($orX);
}
/**
* @param IQueryBuilder $qb
* @param string $field
* @param string|integer $value
*/
public function limitToDBField(IQueryBuilder $qb, $field, $value) {
$expr = $qb->expr();
$pf = ($qb->getType() === QueryBuilder::SELECT) ? $this->default_select_alias . '.' : '';
$qb->andWhere($expr->eq($pf . $field, $qb->createNamedParameter($value)));
}
/**
* @param IQueryBuilder $qb
* @param string $field
* @param string|integer $value
*/
private function limitToDBFieldOrGreater(IQueryBuilder $qb, $field, $value) {
$expr = $qb->expr();
$pf = ($qb->getType() === QueryBuilder::SELECT) ? $this->default_select_alias . '.' : '';
$qb->andWhere($expr->gte($pf . $field, $qb->createNamedParameter($value)));
}
/**
* link to the groupId/UserId of the NC DB.
* If userId is empty, we add the uid of the NCGroup Table in the select list with 'user_id'
* alias
*
* @param IQueryBuilder $qb
* @param string $userId
*/
protected function limitToNCGroupUser(IQueryBuilder $qb, $userId = '') {
$expr = $qb->expr();
$pf = ($qb->getType() === QueryBuilder::SELECT) ? $this->default_select_alias . '.' : '';
$and = $expr->andX($expr->eq($pf . 'user_id', 'ncgu.gid'));
if ($userId !== '') {
$and->add($expr->eq('ncgu.uid', $qb->createNamedParameter($userId)));
} else {
$qb->selectAlias('ncgu.uid', 'user_id');
}
$qb->from(self::NC_TABLE_GROUP_USER, 'ncgu');
$qb->andWhere($and);
}
/**
* Left Join circle table to get more information about the circle.
*
* @param IQueryBuilder $qb
*/
protected function leftJoinCircle(IQueryBuilder $qb) {
if ($qb->getType() !== QueryBuilder::SELECT) {
return;
}
$expr = $qb->expr();
$pf = $this->default_select_alias . '.';
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->selectAlias('lc.type', 'circle_type')
->selectAlias('lc.name', 'circle_name')
->selectAlias('lc.alt_name', 'circle_alt_name')
->selectAlias('lc.settings', 'circle_settings')
->leftJoin(
$this->default_select_alias, DeprecatedRequestBuilder::TABLE_CIRCLES, 'lc',
$expr->eq($pf . 'circle_id', 'lc.unique_id')
);
}
/**
* link to the groupId/UserId of the NC DB.
*
* @param IQueryBuilder $qb
* @param string $userId
* @param string $field
*
* @throws GSStatusException
*/
protected function leftJoinNCGroupAndUser(IQueryBuilder $qb, $userId, $field) {
return;
if (!$this->configService->isLinkedGroupsAllowed()) {
return;
}
$expr = $qb->expr();
$qb->leftJoin(
$this->default_select_alias, self::NC_TABLE_GROUP_USER, 'ncgu',
$expr->eq('ncgu.uid', $qb->createNamedParameter($userId))
);
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->leftJoin(
$this->default_select_alias, DeprecatedRequestBuilder::TABLE_MEMBERS, 'g',
$expr->andX(
$expr->eq('g.user_id', 'ncgu.gid'),
$expr->eq('g.user_type', $qb->createNamedParameter(DeprecatedMember::TYPE_GROUP)),
$expr->eq('g.instance', $qb->createNamedParameter('')),
$expr->eq('g.circle_id', $field)
)
);
$this->leftJoinedNCGroupAndUser = true;
}
}
@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Model\Federated\EventWrapper;
/**
* Class EventWrapperRequest
*
* @package OCA\Circles\Db
*/
class EventWrapperRequest extends EventWrapperRequestBuilder {
/**
* @param EventWrapper $wrapper
*/
public function save(EventWrapper $wrapper): void {
$qb = $this->getEventWrapperInsertSql();
$qb->setValue('token', $qb->createNamedParameter($wrapper->getToken()))
->setValue(
'event', $qb->createNamedParameter(json_encode($wrapper->getEvent(), JSON_UNESCAPED_SLASHES))
)
->setValue(
'result', $qb->createNamedParameter(json_encode($wrapper->getResult(), JSON_UNESCAPED_SLASHES))
)
->setValue('instance', $qb->createNamedParameter($wrapper->getInstance()))
->setValue('interface', $qb->createNamedParameter($wrapper->getInterface()))
->setValue('severity', $qb->createNamedParameter($wrapper->getSeverity()))
->setValue('retry', $qb->createNamedParameter($wrapper->getRetry()))
->setValue('status', $qb->createNamedParameter($wrapper->getStatus()))
->setValue('creation', $qb->createNamedParameter($wrapper->getCreation()));
$qb->execute();
}
/**
* @param EventWrapper $wrapper
*/
public function update(EventWrapper $wrapper): void {
$qb = $this->getEventWrapperUpdateSql();
$qb->set('result', $qb->createNamedParameter(json_encode($wrapper->getResult())))
->set('status', $qb->createNamedParameter($wrapper->getStatus()))
->set('retry', $qb->createNamedParameter($wrapper->getRetry()));
$qb->limitToInstance($wrapper->getInstance());
$qb->limitToToken($wrapper->getToken());
$qb->execute();
}
/**
* @param string $token
* @param int $status
*/
public function updateAll(string $token, int $status): void {
$qb = $this->getEventWrapperUpdateSql();
$qb->set('status', $qb->createNamedParameter($status));
$qb->limitToToken($token);
$qb->execute();
}
/**
* returns unique token not set as FAILED
*
* @return EventWrapper[]
*/
public function getFailedEvents(array $retryRange): array {
$qb = $this->getEventWrapperSelectSql();
$expr = $qb->expr();
$qb->andWhere(
$expr->orX(
$qb->exprLimitInt('status', EventWrapper::STATUS_FAILED),
$expr->andX(
$qb->exprLimitInt('status', EventWrapper::STATUS_INIT),
$qb->exprGt('creation', time() - 86400), // only freshly created; less than 3 hours
$qb->exprLt('creation', time() - 900) // but not too fresh, at least 15 minutes
)
)
);
$qb->gt('retry', $retryRange[0], true);
$qb->lt('retry', $retryRange[1]);
return $this->getItemsFromRequest($qb);
}
/**
* @param string $token
*
* @return EventWrapper[]
*/
public function getByToken(string $token): array {
$qb = $this->getEventWrapperSelectSql();
$qb->limitToToken($token);
return $this->getItemsFromRequest($qb);
}
}
@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Exceptions\RowNotFoundException;
use OCA\Circles\Exceptions\EventWrapperNotFoundException;
use OCA\Circles\Model\Federated\EventWrapper;
/**
* Class GSEventsRequestBuilder
*
* @package OCA\Circles\Db
*/
class EventWrapperRequestBuilder extends CoreRequestBuilder {
/**
* @return CoreQueryBuilder
*/
protected function getEventWrapperInsertSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->insert(self::TABLE_EVENT);
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getEventWrapperUpdateSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->update(self::TABLE_EVENT);
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getEventWrapperSelectSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->generateSelect(
self::TABLE_EVENT,
self::$tables[self::TABLE_EVENT],
CoreQueryBuilder::FEDERATED_EVENT
);
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getEventWrapperDeleteSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->delete(self::TABLE_EVENT);
return $qb;
}
/**
* @param CoreQueryBuilder $qb
*
* @return EventWrapper
* @throws EventWrapperNotFoundException
*/
public function getItemFromRequest(CoreQueryBuilder $qb): EventWrapper {
/** @var EventWrapper $wrapper */
try {
$wrapper = $qb->asItem(EventWrapper::class);
} catch (RowNotFoundException $e) {
throw new EventWrapperNotFoundException();
}
return $wrapper;
}
/**
* @param CoreQueryBuilder $qb
*
* @return EventWrapper[]
*/
public function getItemsFromRequest(CoreQueryBuilder $qb): array {
/** @var EventWrapper[] $result */
return $qb->asItems(EventWrapper::class);
}
}
@@ -0,0 +1,223 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Exceptions\FederatedLinkDoesNotExistException;
use OCA\Circles\Model\FederatedLink;
/**
* @deprecated
* Class FederatedLinksRequest
*
* @package OCA\Circles\Db
*/
class FederatedLinksRequest extends FederatedLinksRequestBuilder {
/**
* @param FederatedLink $link
*
* @return bool
* @throws \Exception
*/
public function create(FederatedLink $link) {
try {
$qb = $this->getLinksInsertSql();
$qb->setValue('status', $qb->createNamedParameter($link->getStatus()))
->setValue('circle_id', $qb->createNamedParameter($link->getCircleId()))
->setValue('unique_id', $qb->createNamedParameter($link->getUniqueId(true)))
->setValue('address', $qb->createNamedParameter($link->getAddress()))
->setValue('token', $qb->createNamedParameter($link->getToken(true)));
$qb->execute();
return true;
} catch (\Exception $e) {
throw $e;
}
}
/**
* @param FederatedLink $link
*/
public function update(FederatedLink $link) {
if ($link->getStatus() === FederatedLink::STATUS_LINK_REMOVE) {
$this->delete($link);
return;
}
$qb = $this->getLinksUpdateSql();
$qb->set('status', $qb->createNamedParameter($link->getStatus()));
if ($link->getUniqueId() !== '') {
$qb->set('unique_id', $qb->createNamedParameter($link->getUniqueId(true)));
}
$this->limitToToken($qb, $link->getToken(true));
$this->limitToCircleId($qb, $link->getCircleId());
$qb->execute();
}
/**
* @param FederatedLink $link
*/
public function delete(FederatedLink $link) {
if ($link === null) {
return;
}
$qb = $this->getLinksDeleteSql();
$this->limitToToken($qb, $link->getToken(true));
$this->limitToCircleId($qb, $link->getCircleId());
$qb->execute();
}
/**
* @param array $data
*
* @return FederatedLink
*/
public function getLinkFromEntry($data) {
if ($data === false || $data === null) {
return null;
}
$link = new FederatedLink();
$link->setId($data['id'])
->setUniqueId($data['unique_id'])
->setStatus($data['status'])
->setAddress($data['address'])
->setToken($data['token'])
->setCircleId($data['circle_id']);
return $link;
}
/**
* returns all FederatedLink from a circle
*
* @param string $circleUniqueId
* @param int $status
*
* @return FederatedLink[]
*/
public function getLinksFromCircle($circleUniqueId, $status = 0) {
$qb = $this->getLinksSelectSql();
$this->limitToCircleId($qb, $circleUniqueId);
$this->limitToStatus($qb, $status);
$links = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$links[] = $this->parseLinksSelectSql($data);
}
$cursor->closeCursor();
return $links;
}
/**
* returns a FederatedLink from a circle identified by its full unique Id
*
* @param string $circleUniqueId
* @param string $linkUniqueId
*
* @return FederatedLink
* @throws FederatedLinkDoesNotExistException
*/
public function getLinkFromCircle($circleUniqueId, $linkUniqueId) {
$qb = $this->getLinksSelectSql();
$this->limitToCircleId($qb, $circleUniqueId);
$this->limitToUniqueId($qb, $linkUniqueId);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new FederatedLinkDoesNotExistException($this->l10n->t('Federated link not found'));
}
return $this->parseLinksSelectSql($data);
}
/**
* return the FederatedLink identified by a remote Circle UniqueId and the Token of the link
*
* @param string $token
* @param string $uniqueId
*
* @return FederatedLink
* @throws FederatedLinkDoesNotExistException
*/
public function getLinkFromToken($token, $uniqueId) {
$qb = $this->getLinksSelectSql();
$this->limitToUniqueId($qb, (string)$uniqueId);
$this->limitToToken($qb, (string)$token);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new FederatedLinkDoesNotExistException($this->l10n->t('Federated link not found'));
}
return $this->parseLinksSelectSql($data);
}
/**
* return the FederatedLink identified by a its Id
*
* @param string $linkUniqueId
*
* @return FederatedLink
* @throws FederatedLinkDoesNotExistException
*/
public function getLinkFromId($linkUniqueId) {
$qb = $this->getLinksSelectSql();
$this->limitToUniqueId($qb, $linkUniqueId);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new FederatedLinkDoesNotExistException($this->l10n->t('Federated link not found'));
}
return $this->parseLinksSelectSql($data);
}
}
@@ -0,0 +1,133 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Model\FederatedLink;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\MiscService;
use OCA\Circles\Service\TimezoneService;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IL10N;
/**
* @deprecated
* Class FederatedLinksRequestBuilder
*
* @package OCA\Circles\Db
*/
class FederatedLinksRequestBuilder extends DeprecatedRequestBuilder {
/**
* CirclesRequestBuilder constructor.
*
* {@inheritdoc}
*/
public function __construct(
IL10N $l10n, IDBConnection $connection, ConfigService $configService,
TimezoneService $timezoneService, MiscService $miscService
) {
parent::__construct($l10n, $connection, $configService, $timezoneService, $miscService);
}
/**
* Base of the Sql Insert request
*
* @return IQueryBuilder
*/
protected function getLinksInsertSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::TABLE_LINKS)
->setValue('creation', $qb->createNamedParameter($this->timezoneService->getUTCDate()));
return $qb;
}
/**
* Base of the Sql Update request
*
* @return IQueryBuilder
*/
protected function getLinksUpdateSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->update(self::TABLE_LINKS);
return $qb;
}
/**
* Base of the Sql Select request for Shares
*
* @return IQueryBuilder
*/
protected function getLinksSelectSql() {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select(
'l.id', 'l.status', 'l.address', 'l.token', 'l.circle_id', 'l.unique_id', 'l.creation'
)
->from(self::TABLE_LINKS, 'l');
$this->default_select_alias = 'l';
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return IQueryBuilder
*/
protected function getLinksDeleteSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::TABLE_LINKS);
return $qb;
}
/**
* @param array $data
*
* @return FederatedLink
*/
protected function parseLinksSelectSql($data) {
$link = new FederatedLink();
$link->setId($data['id'])
->setUniqueId($data['unique_id'])
->setStatus($data['status'])
->setCreation($data['creation'])
->setAddress($data['address'])
->setToken($data['token'])
->setCircleId($data['circle_id']);
return $link;
}
}
@@ -0,0 +1,116 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Model\DeprecatedMember;
/**
* @deprecated
* Class SharesRequest
*
* @package OCA\Circles\Db
*/
class FileSharesRequest extends FileSharesRequestBuilder {
/**
* remove shares from a member to a circle
*
* @param DeprecatedMember $member
*/
public function removeSharesFromMember(DeprecatedMember $member): void {
$qb = $this->getFileSharesDeleteSql();
$expr = $qb->expr();
$andX = $expr->andX();
$andX->add($expr->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE)));
$andX->add($expr->eq('share_with', $qb->createNamedParameter($member->getCircleId())));
$andX->add($expr->eq('uid_initiator', $qb->createNamedParameter($member->getUserId())));
$qb->andWhere($andX);
$qb->execute();
}
/**
* @param string $circleId
*/
public function removeSharesToCircleId(string $circleId): void {
$qb = $this->getFileSharesDeleteSql();
$expr = $qb->expr();
$andX = $expr->andX();
$andX->add($expr->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE)));
$andX->add($expr->eq('share_with', $qb->createNamedParameter($circleId)));
$qb->andWhere($andX);
$qb->execute();
}
/**
* @param string $circleId
*
* @return array
*/
public function getSharesForCircle(string $circleId): array {
$qb = $this->getFileSharesSelectSql();
$this->limitToShareWith($qb, $circleId);
$this->limitToShareType($qb, self::SHARE_TYPE);
$shares = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$shares[] = $data;
}
$cursor->closeCursor();
return $shares;
}
/**
* @return array
*/
public function getShares(): array {
$qb = $this->getFileSharesSelectSql();
$expr = $qb->expr();
$this->limitToShareType($qb, self::SHARE_TYPE);
$qb->andWhere($expr->isNull($this->default_select_alias . '.parent'));
$shares = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$shares[] = $data;
}
$cursor->closeCursor();
return $shares;
}
}
@@ -0,0 +1,91 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* @deprecated
* Class FileSharesRequestBuilder
*
* @package OCA\Circles\Db
*/
class FileSharesRequestBuilder extends DeprecatedRequestBuilder {
/**
* Base of the Sql Delete request
*
* @return IQueryBuilder
*/
protected function getFileSharesDeleteSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::TABLE_FILE_SHARES);
$qb->where(
$qb->expr()
->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE))
);
return $qb;
}
/**
* @return IQueryBuilder
*/
protected function getFileSharesSelectSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select(
's.id',
's.share_with',
's.file_source',
's.uid_owner',
's.uid_initiator',
's.permissions',
's.token',
's.password',
's.file_target'
)
->from(self::TABLE_FILE_SHARES, 's');
$this->default_select_alias = 's';
return $qb;
}
/**
* @return IQueryBuilder
*/
protected function getFileSharesUpdateSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->update(self::TABLE_FILE_SHARES);
return $qb;
}
}
@@ -0,0 +1,218 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Traits\TStringTools;
use OCA\Circles\Model\GlobalScale\GSShare;
use OCA\Circles\Model\GlobalScale\GSShareMountpoint;
use OCA\Circles\Model\DeprecatedMember;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Share\Exceptions\ShareNotFound;
/**
* @deprecated
* Class GSSharesRequest
*
* @package OCA\Circles\Db
*/
class GSSharesRequest extends GSSharesRequestBuilder {
use TStringTools;
/**
* @param GSShare $gsShare
*/
public function create(GSShare $gsShare): void {
$hash = $this->token();
$qb = $this->getGSSharesInsertSql();
$qb->setValue('circle_id', $qb->createNamedParameter($gsShare->getCircleId()))
->setValue('owner', $qb->createNamedParameter($gsShare->getOwner()))
->setValue('instance', $qb->createNamedParameter($gsShare->getInstance()))
->setValue('token', $qb->createNamedParameter($gsShare->getToken()))
->setValue('parent', $qb->createNamedParameter($gsShare->getParent()))
->setValue('mountpoint', $qb->createNamedParameter($gsShare->getMountPoint()))
->setValue('mountpoint_hash', $qb->createNamedParameter($hash));
$qb->execute();
}
/**
* @param string $userId
*
* @return GSShare[]
*/
public function getForUser(string $userId): array {
$qb = $this->getGSSharesSelectSql();
$this->joinMembership($qb, $userId);
$this->leftJoinMountPoint($qb, $userId);
$shares = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$shares[] = $this->parseGSSharesSelectSql($data);
}
$cursor->closeCursor();
return $shares;
}
/**
* @param DeprecatedMember $member
*/
public function removeGSSharesFromMember(DeprecatedMember $member) {
$qb = $this->getGSSharesDeleteSql();
$this->limitToCircleId($qb, $member->getCircleId());
$this->limitToInstance($qb, $member->getInstance());
$this->limitToOwner($qb, $member->getUserId());
$qb->execute();
}
/**
* @param IQueryBuilder $qb
* @param string $userId
*/
private function joinMembership(IQueryBuilder $qb, string $userId) {
$qb->from(DeprecatedRequestBuilder::TABLE_MEMBERS, 'm');
$expr = $qb->expr();
$andX = $expr->andX();
$andX->add($expr->eq('m.user_id', $qb->createNamedParameter($userId)));
$andX->add($expr->eq('m.instance', $qb->createNamedParameter('')));
$andX->add($expr->gt('m.level', $qb->createNamedParameter(0)));
$andX->add($expr->eq('m.user_type', $qb->createNamedParameter(DeprecatedMember::TYPE_USER)));
$andX->add($expr->eq('m.circle_id', 'gsh.circle_id'));
$qb->andWhere($andX);
}
private function leftJoinMountPoint(IQueryBuilder $qb, string $userId) {
$expr = $qb->expr();
$pf = '' . $this->default_select_alias . '.';
$on = $expr->andX();
$on->add($expr->eq('mp.user_id', $qb->createNamedParameter($userId)));
$on->add($expr->eq('mp.share_id', $pf . 'id'));
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->selectAlias('mp.mountPoint', 'gsshares_mountpoint')
->leftJoin($this->default_select_alias, DeprecatedRequestBuilder::TABLE_GSSHARES_MOUNTPOINT, 'mp', $on);
}
/**
* @param string $userId
* @param string $target
*
* @return GSShareMountpoint
* @throws ShareNotFound
*/
public function getShareMountPointByPath(string $userId, string $target): GSShareMountpoint {
$qb = $this->getGSSharesMountpointSelectSql();
$targetHash = md5($target);
$this->limitToUserId($qb, $userId);
$this->limitToMountpointHash($qb, $targetHash);
$shares = [];
$cursor = $qb->execute();
$data = $cursor->fetch();
if ($data === false) {
throw new ShareNotFound();
}
return $this->parseGSSharesMountpointSelectSql($data);
}
/**
* @param int $gsShareId
* @param string $userId
*
* @return GSShareMountpoint
* @throws ShareNotFound
*/
public function getShareMountPointById(int $gsShareId, string $userId): GSShareMountpoint {
$qb = $this->getGSSharesMountpointSelectSql();
$this->limitToShareId($qb, $gsShareId);
$this->limitToUserId($qb, $userId);
$shares = [];
$cursor = $qb->execute();
$data = $cursor->fetch();
if ($data === false) {
throw new ShareNotFound();
}
return $this->parseGSSharesMountpointSelectSql($data);
}
/**
* @param GSShareMountpoint $mountpoint
*/
public function generateShareMountPoint(GSShareMountpoint $mountpoint) {
$qb = $this->getGSSharesMountpointInsertSql();
$hash = ($mountpoint->getMountPoint() === '-') ? '' : md5($mountpoint->getMountPoint());
$qb->setValue('user_id', $qb->createNamedParameter($mountpoint->getUserId()))
->setValue('share_id', $qb->createNamedParameter($mountpoint->getShareId()))
->setValue('mountpoint', $qb->createNamedParameter($mountpoint->getMountPoint()))
->setValue('mountpoint_hash', $qb->createNamedParameter($hash));
$qb->execute();
}
/**
* @param GSShareMountpoint $mountpoint
*
* @return bool
*/
public function updateShareMountPoint(GSShareMountpoint $mountpoint) {
$qb = $this->getGSSharesMountpointUpdateSql();
$hash = ($mountpoint->getMountPoint() === '-') ? '' : md5($mountpoint->getMountPoint());
$qb->set('mountpoint', $qb->createNamedParameter($mountpoint->getMountPoint()))
->set('mountpoint_hash', $qb->createNamedParameter($hash));
$this->limitToShareId($qb, $mountpoint->getShareId());
$this->limitToUserId($qb, $mountpoint->getUserId());
$nb = $qb->execute();
return ($nb === 1);
}
}
@@ -0,0 +1,179 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Model\GlobalScale\GSShare;
use OCA\Circles\Model\GlobalScale\GSShareMountpoint;
use OCP\DB\QueryBuilder\IQueryBuilder;
/** * @deprecated
*
* Class GSSharesRequestBuilder
*
* @package OCA\Circles\Db
*/
class GSSharesRequestBuilder extends DeprecatedRequestBuilder {
/**
* Base of the Sql Insert request for Shares
*
* @return IQueryBuilder
*/
protected function getGSSharesInsertSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::TABLE_GSSHARES);
return $qb;
}
/**
* Base of the Sql Insert request for Shares Mountpoint
*
* @return IQueryBuilder
*/
protected function getGSSharesMountpointInsertSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::TABLE_GSSHARES_MOUNTPOINT);
return $qb;
}
/**
* Base of the Sql Update request
*
* @return IQueryBuilder
*/
protected function getGSSharesUpdateSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->update(self::TABLE_GSSHARES);
return $qb;
}
/**
* @return IQueryBuilder
*/
protected function getGSSharesMountpointUpdateSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->update(self::TABLE_GSSHARES_MOUNTPOINT);
return $qb;
}
/**
* @return IQueryBuilder
*/
protected function getGSSharesSelectSql() {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select(
'gsh.id', 'gsh.circle_id', 'gsh.owner', 'gsh.instance', 'gsh.token', 'gsh.parent',
'gsh.mountpoint', 'gsh.mountpoint_hash'
)
->from(self::TABLE_GSSHARES, 'gsh');
$this->default_select_alias = 'gsh';
return $qb;
}
/**
* @return IQueryBuilder
*/
protected function getGSSharesMountpointSelectSql() {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select(
'gsmp.user_id', 'gsmp.share_id', 'gsmp.mountpoint', 'gsmp.mountpoint_hash'
)
->from(self::TABLE_GSSHARES_MOUNTPOINT, 'gsmp');
$this->default_select_alias = 'gsmp';
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return IQueryBuilder
*/
protected function getGSSharesDeleteSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::TABLE_GSSHARES);
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return IQueryBuilder
*/
protected function getGSSharesMountpointDeleteSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::TABLE_GSSHARES_MOUNTPOINT);
return $qb;
}
/**
* @param array $data
*
* @return GSShare
*/
protected function parseGSSharesSelectSql($data): GSShare {
$share = new GSShare();
$share->importFromDatabase($data);
return $share;
}
/**
* @param array $data
*
* @return GSShareMountpoint
*/
protected function parseGSSharesMountpointSelectSql($data): GSShareMountpoint {
$share = new GSShareMountpoint();
$share->importFromDatabase($data);
return $share;
}
}
@@ -0,0 +1,429 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\IFederatedUser;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\FederatedUser;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Model\Probes\MemberProbe;
/**
* Class MemberRequest
*
* @package OCA\Circles\Db
*/
class MemberRequest extends MemberRequestBuilder {
/**
* @param Member $member
*
* @throws InvalidIdException
*/
public function save(Member $member): void {
$this->confirmValidIds([$member->getCircleId(), $member->getSingleId(), $member->getId()]);
$qb = $this->getMemberInsertSql();
$qb->setValue('circle_id', $qb->createNamedParameter($member->getCircleId()))
->setValue('single_id', $qb->createNamedParameter($member->getSingleId()))
->setValue('member_id', $qb->createNamedParameter($member->getId()))
->setValue('user_id', $qb->createNamedParameter($member->getUserId()))
->setValue('user_type', $qb->createNamedParameter($member->getUserType()))
->setValue('cached_name', $qb->createNamedParameter($member->getDisplayName()))
->setValue('cached_update', $qb->createNamedParameter($this->timezoneService->getUTCDate()))
->setValue('instance', $qb->createNamedParameter($qb->getInstance($member)))
->setValue('level', $qb->createNamedParameter($member->getLevel()))
->setValue('status', $qb->createNamedParameter($member->getStatus()))
->setValue('contact_id', $qb->createNamedParameter($member->getContactId()))
->setValue('note', $qb->createNamedParameter(json_encode($member->getNotes())));
if ($member->hasInvitedBy()) {
$qb->setValue('invited_by', $qb->createNamedParameter($member->getInvitedBy()->getSingleId()));
}
$qb->execute();
}
/**
* @param Member $member
*
* @throws InvalidIdException
*/
public function update(Member $member): void {
$this->confirmValidIds([$member->getCircleId(), $member->getSingleId(), $member->getId()]);
$qb = $this->getMemberUpdateSql();
$qb->set('member_id', $qb->createNamedParameter($member->getId()))
->set('cached_name', $qb->createNamedParameter($member->getDisplayName()))
->set('cached_update', $qb->createNamedParameter($this->timezoneService->getUTCDate()))
->set('level', $qb->createNamedParameter($member->getLevel()))
->set('status', $qb->createNamedParameter($member->getStatus()))
->set('contact_id', $qb->createNamedParameter($member->getContactId()))
->set('note', $qb->createNamedParameter(json_encode($member->getNotes())));
$qb->limitToCircleId($member->getCircleId());
$qb->limitToSingleId($member->getSingleId());
$qb->execute();
}
/**
* @param Member $member
*
* @throws InvalidIdException
* @throws RequestBuilderException
*/
public function insertOrUpdate(Member $member): void {
try {
$this->searchMember($member);
$this->update($member);
} catch (MemberNotFoundException $e) {
$this->save($member);
}
}
/**
* @param string $singleId
* @param string $displayName
* @param string $circleId
*/
public function updateDisplayName(string $singleId, string $displayName, string $circleId = ''): void {
$qb = $this->getMemberUpdateSql();
$qb->set('cached_name', $qb->createNamedParameter($displayName))
->set('cached_update', $qb->createNamedParameter($this->timezoneService->getUTCDate()));
$qb->limitToSingleId($singleId);
if ($circleId !== '') {
$qb->limitToCircleId($circleId);
}
$qb->execute();
}
/**
* @param Member $member
*/
public function delete(Member $member) {
$qb = $this->getMemberDeleteSql();
$qb->limitToCircleId($member->getCircleId());
$qb->limitToSingleId($member->getSingleId());
$qb->execute();
}
/**
* @param IFederatedUser $federatedUser
*/
public function deleteFederatedUser(IFederatedUser $federatedUser): void {
$qb = $this->getMemberDeleteSql();
$qb->limitToSingleId($federatedUser->getSingleId());
$qb->limitToMemberId($federatedUser->getSingleId());
$qb->limitToCircleId($federatedUser->getSingleId());
$qb->execute();
}
/**
* @param IFederatedUser $federatedUser
* @param Circle $circle
*/
public function deleteFederatedUserFromCircle(IFederatedUser $federatedUser, Circle $circle): void {
$qb = $this->getMemberDeleteSql();
$qb->limitToSingleId($federatedUser->getSingleId());
$qb->limitToCircleId($circle->getSingleId());
$qb->execute();
}
/**
*
* @param Circle $circle
*/
public function deleteAllFromCircle(Circle $circle) {
$qb = $this->getMemberDeleteSql();
$qb->andWhere(
$qb->expr()->orX(
$qb->exprLimit('single_id', $circle->getSingleId()),
$qb->exprLimit('circle_id', $circle->getSingleId())
)
);
$qb->execute();
}
/**
* @param Member $member
*/
public function updateLevel(Member $member): void {
$qb = $this->getMemberUpdateSql();
$qb->set('level', $qb->createNamedParameter($member->getLevel()));
$qb->limitToMemberId($member->getId());
$qb->limitToCircleId($member->getCircleId());
$qb->limitToSingleId($member->getSingleId());
$qb->execute();
}
/**
* @param string $singleId
* @param IFederatedUser|null $initiator
* @param MemberProbe|null $probe
*
* @return Member[]
* @throws RequestBuilderException
*/
public function getMembers(
string $singleId,
?IFederatedUser $initiator = null,
?MemberProbe $probe = null
): array {
if (is_null($probe)) {
$probe = new MemberProbe();
}
$qb = $this->getMemberSelectSql($initiator);
$qb->limitToCircleId($singleId);
$qb->setOptions(
[CoreQueryBuilder::MEMBER],
array_merge(
$probe->getAsOptions(),
['viewableThroughKeyhole' => true]
)
);
$qb->leftJoinCircle(CoreQueryBuilder::MEMBER, $initiator);
$qb->leftJoinInvitedBy(CoreQueryBuilder::MEMBER);
if ($probe->hasFilterRemoteInstance()) {
$aliasCircle = $qb->generateAlias(CoreQueryBuilder::MEMBER, CoreQueryBuilder::CIRCLE);
$qb->limitToRemoteInstance(
CoreQueryBuilder::MEMBER,
$probe->getFilterRemoteInstance(),
true,
$aliasCircle
);
}
if ($probe->hasFilterMember()) {
$qb->filterDirectMembership(CoreQueryBuilder::MEMBER, $probe->getFilterMember());
}
$qb->orderBy($qb->getDefaultSelectAlias() . '.level', 'desc');
$qb->addOrderBy($qb->getDefaultSelectAlias() . '.cached_name', 'asc');
return $this->getItemsFromRequest($qb);
}
/**
* @param string $singleId
* @param bool $getData
* @param int $level
*
* @return Member[]
* @throws RequestBuilderException
*/
public function getInheritedMembers(string $singleId, bool $getData = false, int $level = 0): array {
$qb = $this->getMemberSelectSql(null, $getData);
if ($getData) {
$qb->leftJoinCircle(CoreQueryBuilder::MEMBER);
$qb->setOptions([CoreQueryBuilder::MEMBER], ['getData' => $getData]);
}
$qb->limitToMembersByInheritance(CoreQueryBuilder::MEMBER, $singleId, $level);
$aliasMembership = $qb->generateAlias(CoreQueryBuilder::MEMBER, CoreQueryBuilder::MEMBERSHIPS);
$qb->orderBy($aliasMembership . '.inheritance_depth', 'asc');
return $this->getItemsFromRequest($qb);
}
/**
* @param string $circleId
* @param string $singleId
* @param CircleProbe|null $probe
*
* @return Member
* @throws MemberNotFoundException
* @throws RequestBuilderException
*/
public function getMember(string $circleId, string $singleId, ?CircleProbe $probe = null): Member {
$qb = $this->getMemberSelectSql();
$qb->limitToCircleId($circleId);
$qb->limitToSingleId($singleId);
if (!is_null($probe)) {
$qb->setOptions([CoreQueryBuilder::MEMBER], $probe->getAsOptions());
}
return $this->getItemFromRequest($qb);
}
/**
* @param string $memberId
* @param FederatedUser|null $initiator
* @param MemberProbe|null $probe
*
* @return Member
* @throws MemberNotFoundException
* @throws RequestBuilderException
*/
public function getMemberById(
string $memberId,
?FederatedUser $initiator = null,
?MemberProbe $probe = null
): Member {
if (is_null($probe)) {
$probe = new MemberProbe();
}
$qb = $this->getMemberSelectSql();
$qb->limitToMemberId($memberId);
$qb->setOptions([CoreQueryBuilder::MEMBER], $probe->getAsOptions());
if (!is_null($initiator)) {
$qb->leftJoinCircle(CoreQueryBuilder::MEMBER, $initiator);
}
return $this->getItemFromRequest($qb);
}
/**
* @param string $circleId
*
* @return array
* @throws RequestBuilderException
*/
public function getMemberInstances(string $circleId): array {
$qb = $this->getMemberSelectSql();
$qb->limitToCircleId($circleId);
$qb->andwhere($qb->expr()->nonEmptyString(CoreQueryBuilder::MEMBER . '.instance'));
return array_map(
function (Member $member): string {
return $member->getInstance();
}, $this->getItemsFromRequest($qb)
);
}
/**
* @param string $singleId
*
* @return Member[]
* @throws RequestBuilderException
*/
public function getMembersBySingleId(string $singleId): array {
$qb = $this->getMemberSelectSql();
$qb->leftJoinCircle(CoreQueryBuilder::MEMBER);
$qb->limitToSingleId($singleId);
return $this->getItemsFromRequest($qb);
}
/**
* @param Member $member
* @param FederatedUser|null $initiator
*
* @return Member
* @throws MemberNotFoundException
* @throws RequestBuilderException
*/
public function searchMember(Member $member, ?FederatedUser $initiator = null): Member {
$qb = $this->getMemberSelectSql();
$qb->limitToCircleId($member->getCircleId());
$qb->limitToSingleId($member->getSingleId());
$qb->leftJoinCircle(CoreQueryBuilder::MEMBER, $initiator);
return $this->getItemFromRequest($qb);
}
/**
* @param string $needle
*
* @return FederatedUser[]
* @throws RequestBuilderException
*/
public function searchFederatedUsers(string $needle): array {
$qb = $this->getMemberSelectSql();
$qb->searchInDBField('user_id', '%' . $needle . '%');
return $this->getItemsFromRequest($qb, true);
}
/**
* @param IFederatedUser $federatedUser
*
* @return Member[]
* @throws RequestBuilderException
*/
public function getAlternateSingleId(IFederatedUser $federatedUser): array {
$qb = $this->getMemberSelectSql();
$qb->limitToSingleId($federatedUser->getSingleId());
$qb->leftJoinRemoteInstance(CoreQueryBuilder::MEMBER);
// $expr = $qb->expr();
// $orX = $expr->orX(
// $qb->exprFilter('user_id', $federatedUser->getUserId()),
// $qb->exprFilterInt('user_type', $federatedUser->getUserType()),
// $qb->exprFilter('instance', $qb->getInstance($federatedUser), '', false)
// );
//
// $qb->andWhere($orX);
return $this->getItemsFromRequest($qb);
}
}
@@ -0,0 +1,142 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Exceptions\MemberNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\IFederatedUser;
use OCA\Circles\Model\FederatedUser;
use OCA\Circles\Model\Member;
use OCA\Circles\Tools\Exceptions\RowNotFoundException;
/**
* Class MemberRequestBuilder
*
* @package OCA\Circles\Db
*/
class MemberRequestBuilder extends CoreRequestBuilder {
/**
* @return CoreQueryBuilder
*/
protected function getMemberInsertSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->insert(self::TABLE_MEMBER)
->setValue('joined', $qb->createNamedParameter($this->timezoneService->getUTCDate()));
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getMemberUpdateSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->update(self::TABLE_MEMBER);
return $qb;
}
/**
* @param IFederatedUser|null $initiator
* @param bool $getBasedOn
*
* @return CoreQueryBuilder
* @throws RequestBuilderException
*/
protected function getMemberSelectSql(
?IFederatedUser $initiator = null,
bool $getBasedOn = true
): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->generateSelect(
self::TABLE_MEMBER,
self::$tables[self::TABLE_MEMBER],
CoreQueryBuilder::MEMBER
)
->orderBy(CoreQueryBuilder::MEMBER . '.joined');
if ($getBasedOn) {
$qb->leftJoinBasedOn(CoreQueryBuilder::MEMBER, $initiator);
}
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return CoreQueryBuilder
*/
protected function getMemberDeleteSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->delete(self::TABLE_MEMBER);
return $qb;
}
/**
* @param CoreQueryBuilder $qb
*
* @return Member
* @throws MemberNotFoundException
*/
public function getItemFromRequest(CoreQueryBuilder $qb): Member {
/** @var Member $member */
try {
$member = $qb->asItem(Member::class);
} catch (RowNotFoundException $e) {
throw new MemberNotFoundException();
}
return $member;
}
/**
* @param CoreQueryBuilder $qb
* @param bool $asFederatedUser
*
* @return Member[]|FederatedUser[]
*/
public function getItemsFromRequest(CoreQueryBuilder $qb, bool $asFederatedUser = false): array {
$object = Member::class;
if ($asFederatedUser) {
$object = FederatedUser::class;
}
/** @var Member|FederatedUser[] $result */
return $qb->asItems($object);
}
}
@@ -0,0 +1,175 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Exceptions\MembershipNotFoundException;
use OCA\Circles\Model\FederatedUser;
use OCA\Circles\Model\Membership;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* Class MembershipRequest
*
* @package OCA\Circles\Db
*/
class MembershipRequest extends MembershipRequestBuilder {
/**
* @param Membership $membership
*/
public function insert(Membership $membership) {
$qb = $this->getMembershipInsertSql();
$qb->setValue('circle_id', $qb->createNamedParameter($membership->getCircleId()));
$qb->setValue('single_id', $qb->createNamedParameter($membership->getSingleId()));
$qb->setValue('level', $qb->createNamedParameter($membership->getLevel()));
$qb->setValue('inheritance_first', $qb->createNamedParameter($membership->getInheritanceFirst()));
$qb->setValue('inheritance_last', $qb->createNamedParameter($membership->getInheritanceLast()));
$qb->setValue(
'inheritance_path',
$qb->createNamedParameter(json_encode($membership->getInheritancePath(), JSON_UNESCAPED_SLASHES))
);
$qb->setValue('inheritance_depth', $qb->createNamedParameter($membership->getInheritanceDepth()));
$qb->execute();
}
/**
* @param Membership $membership
*/
public function update(Membership $membership) {
$qb = $this->getMembershipUpdateSql();
$qb->set('level', $qb->createNamedParameter($membership->getLevel()));
$qb->set('inheritance_last', $qb->createNamedParameter($membership->getInheritanceLast()));
$qb->set('inheritance_first', $qb->createNamedParameter($membership->getInheritanceFirst()));
$qb->set(
'inheritance_path',
$qb->createNamedParameter(json_encode($membership->getInheritancePath(), JSON_UNESCAPED_SLASHES))
);
$qb->set('inheritance_depth', $qb->createNamedParameter($membership->getInheritanceDepth()));
$qb->limitToSingleId($membership->getSingleId());
$qb->limitToCircleId($membership->getCircleId());
$qb->execute();
}
/**
* @param string $circleId
* @param string $singleId
*
* @return Membership
* @throws MembershipNotFoundException
*/
public function getMembership(string $circleId, string $singleId): Membership {
$qb = $this->getMembershipSelectSql();
$qb->limitToCircleId($circleId);
$qb->limitToSingleId($singleId);
$qb->leftJoinCircleConfig(self::TABLE_MEMBERSHIP);
return $this->getItemFromRequest($qb);
}
/**
* @param string $singleId
*
* @return Membership[]
*/
public function getMemberships(string $singleId): array {
$qb = $this->getMembershipSelectSql();
$qb->limitToSingleId($singleId);
$qb->leftJoinCircleConfig(CoreQueryBuilder::MEMBERSHIPS);
return $this->getItemsFromRequest($qb);
}
/**
* @param string $singleId
* @param int $level
*
* @return Membership[]
*/
public function getInherited(string $singleId, int $level = 0): array {
$qb = $this->getMembershipSelectSql();
$qb->limitToCircleId($singleId);
$qb->leftJoinCircleConfig(self::TABLE_MEMBERSHIP);
if ($level > 1) {
$expr = $qb->expr();
$qb->andWhere($expr->gte('level', $qb->createNamedParameter($level, IQueryBuilder::PARAM_INT)));
}
return $this->getItemsFromRequest($qb);
}
/**
* @param string $singleId
* @param bool $all
*
* @return void
*/
public function removeBySingleId(string $singleId, bool $all = false): void {
$qb = $this->getMembershipDeleteSql();
if (!$all) {
$qb->limitToSingleId($singleId);
}
$qb->execute();
}
/**
* @param Membership $membership
*/
public function delete(Membership $membership): void {
$qb = $this->getMembershipDeleteSql();
$qb->limitToSingleId($membership->getSingleId());
$qb->limitToCircleId($membership->getCircleId());
$qb->execute();
}
/**
* @param FederatedUser $federatedUser
*/
public function deleteFederatedUser(FederatedUser $federatedUser): void {
$qb = $this->getMembershipDeleteSql();
$qb->limitToSingleId($federatedUser->getSingleId());
$qb->execute();
}
}
@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Exceptions\RowNotFoundException;
use OCA\Circles\Exceptions\MembershipNotFoundException;
use OCA\Circles\Model\Membership;
/**
* Class MembershipRequestBuilder
*
* @package OCA\Circles\Db
*/
class MembershipRequestBuilder extends CoreRequestBuilder {
/**
* @return CoreQueryBuilder
*/
protected function getMembershipInsertSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->insert(self::TABLE_MEMBERSHIP);
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getMembershipUpdateSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->update(self::TABLE_MEMBERSHIP);
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getMembershipSelectSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->generateSelect(
self::TABLE_MEMBERSHIP,
self::$tables[self::TABLE_MEMBERSHIP],
CoreQueryBuilder::MEMBERSHIPS
);
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return CoreQueryBuilder
*/
protected function getMembershipDeleteSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->delete(self::TABLE_MEMBERSHIP);
return $qb;
}
/**
* @param CoreQueryBuilder $qb
*
* @return Membership
* @throws MembershipNotFoundException
*/
public function getItemFromRequest(CoreQueryBuilder $qb): Membership {
/** @var Membership $membership */
try {
$membership = $qb->asItem(Membership::class);
} catch (RowNotFoundException $e) {
throw new MembershipNotFoundException();
}
return $membership;
}
/**
* @param CoreQueryBuilder $qb
*
* @return Membership[]
*/
public function getItemsFromRequest(CoreQueryBuilder $qb): array {
/** @var Membership[] $result */
return $qb->asItems(Membership::class);
}
}
@@ -0,0 +1,285 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Traits\TStringTools;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\IFederatedUser;
use OCA\Circles\Model\Mount;
/**
* Class MountRequest
*
* @package OCA\Circles\Db
*/
class MountRequest extends MountRequestBuilder {
use TStringTools;
/**
* @param Mount $mount
*/
public function save(Mount $mount): void {
$qb = $this->getMountInsertSql();
$qb->setValue('circle_id', $qb->createNamedParameter($mount->getCircleId()))
->setValue('mount_id', $qb->createNamedParameter($mount->getMountId()))
->setValue('single_id', $qb->createNamedParameter($mount->getOwner()->getSingleId()))
->setValue('token', $qb->createNamedParameter($mount->getToken()))
->setValue('parent', $qb->createNamedParameter($mount->getParent()))
->setValue('mountpoint', $qb->createNamedParameter($mount->getMountPoint()))
->setValue('mountpoint_hash', $qb->createNamedParameter(md5($mount->getMountPoint())));
$qb->execute();
}
/**
* @param string $token
*/
public function delete(string $token): void {
$qb = $this->getMountDeleteSql();
$qb->limitToToken($token);
$qb->execute();
}
/**
* @param IFederatedUser $federatedUser
*
* @return Mount[]
* @throws RequestBuilderException
*/
public function getForUser(IFederatedUser $federatedUser): array {
$qb = $this->getMountSelectSql();
$qb->setOptions([CoreQueryBuilder::MOUNT], ['getData' => true]);
$qb->leftJoinMember(CoreQueryBuilder::MOUNT);
$qb->leftJoinMountpoint(CoreQueryBuilder::MOUNT);
$qb->limitToInitiator(CoreQueryBuilder::MOUNT, $federatedUser, 'circle_id');
return $this->getItemsFromRequest($qb);
// FederatedUser $federatedUser,
// int $nodeId,
// int $offset,
// int $limit,
// bool $getData = false
// ): array {
// $qb = $this->getShareSelectSql();
// $qb->setOptions([CoreRequestBuilder::SHARE], ['getData' => $getData]);
// if ($getData) {
// $qb->leftJoinCircle(CoreRequestBuilder::SHARE, null, 'share_with');
// }
//
// $qb->limitToInitiator(CoreRequestBuilder::SHARE, $federatedUser, 'share_with');
//
// $qb->leftJoinFileCache(CoreRequestBuilder::SHARE);
// $qb->limitToDBFieldEmpty('parent', true);
// $qb->leftJoinShareChild(CoreRequestBuilder::SHARE);
//
// if ($nodeId > 0) {
// $qb->limitToFileSource($nodeId);
// }
//
// $qb->chunk($offset, $limit);
//
// return $this->getItemsFromRequest($qb);
// $this->joinMembership($qb, $userId);
// $this->leftJoinMountPoint($qb, $userId);
// $shares = [];
// $cursor = $qb->execute();
// while ($data = $cursor->fetch()) {
// $shares[] = $this->parseGSSharesSelectSql($data);
// }
// $cursor->closeCursor();
//
// return $shares;
}
// /**
// * @param string $userId
// *
// * @return Mount[]
// */
// public function getForUser(string $userId): array {
// $qb = $this->getMountSelectSql();
//
// $this->
// $this->joinMembership($qb, $userId);
// $this->leftJoinMountPoint($qb, $userId);
//
// $shares = [];
// $cursor = $qb->execute();
// while ($data = $cursor->fetch()) {
// $shares[] = $this->parseGSSharesSelectSql($data);
// }
// $cursor->closeCursor();
//
// return $shares;
// }
//
// /**
// * @param DeprecatedMember $member
// */
// public function removeGSSharesFromMember(DeprecatedMember $member) {
// $qb = $this->getMountDeleteSql();
// $this->limitToCircleId($qb, $member->getCircleId());
// $this->limitToInstance($qb, $member->getInstance());
// $this->limitToOwner($qb, $member->getUserId());
//
// $qb->execute();
// }
//
//
// /**
// * @param IQueryBuilder $qb
// * @param string $userId
// */
// private function joinMembership(IQueryBuilder $qb, string $userId) {
// $qb->from(DeprecatedRequestBuilder::TABLE_MEMBERS, 'm');
//
// $expr = $qb->expr();
// $andX = $expr->andX();
//
// $andX->add($expr->eq('m.user_id', $qb->createNamedParameter($userId)));
// $andX->add($expr->eq('m.instance', $qb->createNamedParameter('')));
// $andX->add($expr->gt('m.level', $qb->createNamedParameter(0)));
// $andX->add($expr->eq('m.user_type', $qb->createNamedParameter(DeprecatedMember::TYPE_USER)));
// $andX->add($expr->eq('m.circle_id', 'gsh.circle_id'));
//
// $qb->andWhere($andX);
// }
//
//
// private function leftJoinMountPoint(IQueryBuilder $qb, string $userId) {
// $expr = $qb->expr();
// $pf = '' . $this->default_select_alias . '.';
//
// $on = $expr->andX();
// $on->add($expr->eq('mp.user_id', $qb->createNamedParameter($userId)));
// $on->add($expr->eq('mp.share_id', $pf . 'id'));
//
// /** @noinspection PhpMethodParametersCountMismatchInspection */
// $qb->selectAlias('mp.mountPoint', 'gsshares_mountpoint')
// ->leftJoin($this->default_select_alias, DeprecatedRequestBuilder::TABLE_GSSHARES_MOUNTPOINT, 'mp', $on);
// }
//
//
// /**
// * @param string $userId
// * @param string $target
// *
// * @return GSShareMountpoint
// * @throws ShareNotFound
// */
// public function getShareMountPointByPath(string $userId, string $target): GSShareMountpoint {
// $qb = $this->getMountMountpointSelectSql();
//
// $targetHash = md5($target);
// $this->limitToUserId($qb, $userId);
// $this->limitToMountpointHash($qb, $targetHash);
//
// $shares = [];
// $cursor = $qb->execute();
// $data = $cursor->fetch();
//
// if ($data === false) {
// throw new ShareNotFound();
// }
//
// return $this->parseGSSharesMountpointSelectSql($data);
// }
//
//
// /**
// * @param int $gsShareId
// * @param string $userId
// *
// * @return GSShareMountpoint
// * @throws ShareNotFound
// */
// public function getShareMountPointById(int $gsShareId, string $userId): GSShareMountpoint {
// $qb = $this->getMountMountpointSelectSql();
//
// $this->limitToShareId($qb, $gsShareId);
// $this->limitToUserId($qb, $userId);
//
// $shares = [];
// $cursor = $qb->execute();
// $data = $cursor->fetch();
// if ($data === false) {
// throw new ShareNotFound();
// }
//
// return $this->parseGSSharesMountpointSelectSql($data);
// }
//
//
// /**
// * @param GSShareMountpoint $mountpoint
// */
// public function generateShareMountPoint(GSShareMountpoint $mountpoint) {
// $qb = $this->getMountMountpointInsertSql();
//
// $hash = ($mountpoint->getMountPoint() === '-') ? '' : md5($mountpoint->getMountPoint());
//
// $qb->setValue('user_id', $qb->createNamedParameter($mountpoint->getUserId()))
// ->setValue('share_id', $qb->createNamedParameter($mountpoint->getShareId()))
// ->setValue('mountpoint', $qb->createNamedParameter($mountpoint->getMountPoint()))
// ->setValue('mountpoint_hash', $qb->createNamedParameter($hash));
// $qb->execute();
// }
//
//
// /**
// * @param GSShareMountpoint $mountpoint
// *
// * @return bool
// */
// public function updateShareMountPoint(GSShareMountpoint $mountpoint) {
// $qb = $this->getMountMountpointUpdateSql();
//
// $hash = ($mountpoint->getMountPoint() === '-') ? '' : md5($mountpoint->getMountPoint());
//
// $qb->set('mountpoint', $qb->createNamedParameter($mountpoint->getMountPoint()))
// ->set('mountpoint_hash', $qb->createNamedParameter($hash));
//
// $this->limitToShareId($qb, $mountpoint->getShareId());
// $this->limitToUserId($qb, $mountpoint->getUserId());
// $nb = $qb->execute();
//
// return ($nb === 1);
// }
//
}
@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Exceptions\RowNotFoundException;
use OCA\Circles\Exceptions\MountNotFoundException;
use OCA\Circles\Model\Mount;
/**
* Class MountRequestBuilder
*
* @package OCA\Circles\Db
*/
class MountRequestBuilder extends CoreRequestBuilder {
/**
* @return CoreQueryBuilder
*/
protected function getMountInsertSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->insert(self::TABLE_MOUNT);
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getMountUpdateSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->update(self::TABLE_MOUNT);
return $qb;
}
/**
* @param string $alias
*
* @return CoreQueryBuilder
*/
protected function getMountSelectSql(string $alias = CoreQueryBuilder::MOUNT): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->generateSelect(self::TABLE_MOUNT, self::$tables[self::TABLE_MOUNT], $alias);
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getMountDeleteSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->delete(self::TABLE_MOUNT);
return $qb;
}
/**
* @param CoreQueryBuilder $qb
*
* @return Mount
* @throws MountNotFoundException
*/
public function getItemFromRequest(CoreQueryBuilder $qb): Mount {
/** @var Mount $circle */
try {
$circle = $qb->asItem(Mount::class);
} catch (RowNotFoundException $e) {
throw new MountNotFoundException('Mount not found');
}
return $circle;
}
/**
* @param CoreQueryBuilder $qb
*
* @return Mount[]
*/
public function getItemsFromRequest(CoreQueryBuilder $qb): array {
/** @var Mount[] $result */
return $qb->asItems(Mount::class);
}
}
@@ -0,0 +1,286 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteUidException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Federated\RemoteInstance;
use OCA\Circles\Model\Member;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* Class RemoteRequest
*
* @package OCA\Circles\Db
*/
class RemoteRequest extends RemoteRequestBuilder {
/**
* @param RemoteInstance $remote
*
* @throws RemoteUidException
*/
public function save(RemoteInstance $remote): void {
$remote->mustBeIdentityAuthed();
$qb = $this->getRemoteInsertSql();
$qb->setValue('uid', $qb->createNamedParameter($remote->getUid(true)))
->setValue('instance', $qb->createNamedParameter($remote->getInstance()))
->setValue('href', $qb->createNamedParameter($remote->getId()))
->setValue('type', $qb->createNamedParameter($remote->getType()))
->setValue('interface', $qb->createNamedParameter($remote->getInterface()))
->setValue('item', $qb->createNamedParameter(json_encode($remote->getOrigData())));
$qb->execute();
}
/**
* @param RemoteInstance $remote
*
* @throws RemoteUidException
*/
public function update(RemoteInstance $remote) {
$remote->mustBeIdentityAuthed();
$qb = $this->getRemoteUpdateSql();
$qb->set('uid', $qb->createNamedParameter($remote->getUid(true)))
->set('href', $qb->createNamedParameter($remote->getId()))
->set('type', $qb->createNamedParameter($remote->getType()))
->set('item', $qb->createNamedParameter(json_encode($remote->getOrigData())));
$qb->limitToInstance($remote->getInstance());
$qb->execute();
}
/**
* @param RemoteInstance $remote
*
* @throws RemoteUidException
*/
public function updateItem(RemoteInstance $remote) {
$remote->mustBeIdentityAuthed();
$qb = $this->getRemoteUpdateSql();
$qb->set('item', $qb->createNamedParameter(json_encode($remote->getOrigData())));
$qb->limit('uid', $remote->getUid(true), '', false);
$qb->execute();
}
/**
* @param RemoteInstance $remote
*
* @throws RemoteUidException
*/
public function updateInstance(RemoteInstance $remote) {
$remote->mustBeIdentityAuthed();
$qb = $this->getRemoteUpdateSql();
$qb->set('instance', $qb->createNamedParameter($remote->getInstance()));
$qb->limit('uid', $remote->getUid(true), '', false);
$qb->execute();
}
/**
* @param RemoteInstance $remote
*
* @throws RemoteUidException
*/
public function updateType(RemoteInstance $remote) {
$remote->mustBeIdentityAuthed();
$qb = $this->getRemoteUpdateSql();
$qb->set('type', $qb->createNamedParameter($remote->getType()));
$qb->limit('uid', $remote->getUid(true), '', false);
$qb->execute();
}
/**
* @param RemoteInstance $remote
*
* @throws RemoteUidException
*/
public function updateHref(RemoteInstance $remote) {
$remote->mustBeIdentityAuthed();
$qb = $this->getRemoteUpdateSql();
$qb->set('href', $qb->createNamedParameter($remote->getId()));
$qb->limit('uid', $remote->getUid(true), '', false);
$qb->execute();
}
/**
* @return RemoteInstance[]
*/
public function getAllInstances(): array {
$qb = $this->getRemoteSelectSql();
return $this->getItemsFromRequest($qb);
}
/**
* @return RemoteInstance[]
*/
public function getKnownInstances(): array {
$qb = $this->getRemoteSelectSql();
$qb->filter('type', RemoteInstance::TYPE_UNKNOWN, '', false);
return $this->getItemsFromRequest($qb);
}
/**
* - returns:
* - all GLOBAL_SCALE
* - TRUSTED if Circle is Federated
* - EXTERNAL if Circle is Federated and a contains a member from instance
*
* @param Circle $circle
* @param bool $broadcastAsFederated
*
* @return RemoteInstance[]
* @throws RequestBuilderException
*/
public function getOutgoingRecipient(Circle $circle, bool $broadcastAsFederated = false): array {
$qb = $this->getRemoteSelectSql();
$expr = $qb->expr();
$orX = $expr->orX();
$orX->add($qb->exprLimit('type', RemoteInstance::TYPE_GLOBALSCALE, '', false));
if ($circle->isConfig(Circle::CFG_FEDERATED) || $broadcastAsFederated) {
// get all TRUSTED
$orX->add($qb->exprLimit('type', RemoteInstance::TYPE_TRUSTED, '', false));
// get EXTERNAL with Members
$aliasMember = $qb->generateAlias(CoreQueryBuilder::REMOTE, CoreQueryBuilder::MEMBER);
$qb->leftJoin(
CoreQueryBuilder::REMOTE, self::TABLE_MEMBER, $aliasMember,
$expr->andX(
$expr->eq($aliasMember . '.circle_id', $qb->createNamedParameter($circle->getSingleId())),
$expr->eq($aliasMember . '.instance', CoreQueryBuilder::REMOTE . '.instance'),
$expr->gte(
$aliasMember . '.level',
$qb->createNamedParameter(Member::LEVEL_MEMBER, IQueryBuilder::PARAM_INT)
)
)
);
$external = $expr->andX();
$external->add($qb->exprLimit('type', RemoteInstance::TYPE_EXTERNAL, '', false));
$external->add($expr->isNotNull($aliasMember . '.instance'));
$orX->add($external);
}
$qb->andWhere($orX);
return $this->getItemsFromRequest($qb);
}
/**
* @param string $host
*
* @return RemoteInstance
* @throws RemoteNotFoundException
*/
public function getFromInstance(string $host): RemoteInstance {
$qb = $this->getRemoteSelectSql();
$qb->limitToInstance($host);
return $this->getItemFromRequest($qb);
}
/**
* @param string $href
*
* @return RemoteInstance
* @throws RemoteNotFoundException
*/
public function getFromHref(string $href): RemoteInstance {
$qb = $this->getRemoteSelectSql();
$qb->limit('href', $href, '', false);
return $this->getItemFromRequest($qb);
}
/**
* @param string $status
*
* @return RemoteInstance[]
*/
public function getFromType(string $status): array {
$qb = $this->getRemoteSelectSql();
$qb->limitToTypeString($status);
return $this->getItemsFromRequest($qb);
}
/**
* @param RemoteInstance $remoteInstance
*
* @return RemoteInstance
* @throws RemoteNotFoundException
*/
public function searchDuplicate(RemoteInstance $remoteInstance): RemoteInstance {
$qb = $this->getRemoteSelectSql();
$orX = $qb->expr()->orX();
$orX->add($qb->exprLimit('href', $remoteInstance->getId(), '', false));
$orX->add($qb->exprLimit('uid', $remoteInstance->getUid(true), '', false));
$orX->add($qb->exprLimit('instance', $remoteInstance->getInstance(), '', false));
$qb->andWhere($orX);
return $this->getItemFromRequest($qb);
}
/**
* @param RemoteInstance $remoteInstance
*/
public function deleteById(RemoteInstance $remoteInstance) {
$qb = $this->getRemoteDeleteSql();
$qb->limitToId($remoteInstance->getDbId());
$qb->execute();
}
}
@@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Exceptions\RowNotFoundException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Model\Federated\RemoteInstance;
/**
* Class RemoteRequestBuilder
*
* @package OCA\Circles\Db
*/
class RemoteRequestBuilder extends CoreRequestBuilder {
/**
* @return CoreQueryBuilder
*/
protected function getRemoteInsertSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->insert(self::TABLE_REMOTE)
->setValue('creation', $qb->createNamedParameter($this->timezoneService->getUTCDate()));
return $qb;
}
/**
* Base of the Sql Update request for Groups
*
* @return CoreQueryBuilder
*/
protected function getRemoteUpdateSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->update(self::TABLE_REMOTE);
return $qb;
}
/**
* @param string $alias
*
* @return CoreQueryBuilder
*/
protected function getRemoteSelectSql(string $alias = CoreQueryBuilder::REMOTE): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->generateSelect(self::TABLE_REMOTE, self::$tables[self::TABLE_REMOTE], $alias);
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return CoreQueryBuilder
*/
protected function getRemoteDeleteSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->delete(self::TABLE_REMOTE);
return $qb;
}
/**
* @param CoreQueryBuilder $qb
*
* @return RemoteInstance
* @throws RemoteNotFoundException
*/
public function getItemFromRequest(CoreQueryBuilder $qb): RemoteInstance {
/** @var RemoteInstance $appService */
try {
$appService = $qb->asItem(RemoteInstance::class);
} catch (RowNotFoundException $e) {
throw new RemoteNotFoundException('Unknown remote instance');
}
return $appService;
}
/**
* @param CoreQueryBuilder $qb
*
* @return RemoteInstance[]
*/
public function getItemsFromRequest(CoreQueryBuilder $qb): array {
/** @var RemoteInstance[] $result */
return $qb->asItems(RemoteInstance::class);
}
}
@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Exceptions\FederatedShareNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Model\Federated\FederatedShare;
/**
* Class ShareRequest
*
* @package OCA\Circles\Db
*/
class ShareLockRequest extends ShareLockRequestBuilder {
/**
* @param FederatedShare $share
*
* @throws InvalidIdException
*/
public function save(FederatedShare $share): void {
$this->confirmValidIds([$share->getItemId()]);
$qb = $this->getShareLockInsertSql();
$qb->setValue('item_id', $qb->createNamedParameter($share->getItemId()))
->setValue('circle_id', $qb->createNamedParameter($share->getCircleId()))
->setValue('instance', $qb->createNamedParameter($qb->getInstance($share)));
$qb->execute();
}
/**
* @param string $itemId
* @param string $circleId
*
* @return FederatedShare
* @throws FederatedShareNotFoundException
*/
public function getShare(string $itemId, string $circleId = ''): FederatedShare {
$qb = $this->getShareLockSelectSql();
$qb->limitToItemId($itemId);
if ($circleId !== '') {
$qb->limitToCircleId($circleId);
}
return $this->getItemFromRequest($qb);
}
}
@@ -0,0 +1,117 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Exceptions\RowNotFoundException;
use OCA\Circles\Exceptions\FederatedShareNotFoundException;
use OCA\Circles\Model\Federated\FederatedShare;
/**
* Class ShareRequestBuilder
*
* @package OCA\Circles\Db
*/
class ShareLockRequestBuilder extends CoreRequestBuilder {
/**
* @return CoreQueryBuilder
*/
protected function getShareLockInsertSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->insert(self::TABLE_SHARE_LOCK);
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getShareLockSelectSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->select('s.id', 's.item_id', 's.circle_id', 's.instance')
->from(self::TABLE_SHARE_LOCK, 's')
->setDefaultSelectAlias('s');
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getShareLockUpdateSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->update(self::TABLE_SHARE_LOCK);
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getShareDeleteSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->delete(self::TABLE_SHARE_LOCK);
return $qb;
}
/**
* @param CoreQueryBuilder $qb
*
* @return FederatedShare
* @throws FederatedShareNotFoundException
*/
public function getItemFromRequest(CoreQueryBuilder $qb): FederatedShare {
/** @var FederatedShare $circle */
try {
$circle = $qb->asItem(FederatedShare::class);
} catch (RowNotFoundException $e) {
throw new FederatedShareNotFoundException();
}
return $circle;
}
/**
* @param CoreQueryBuilder $qb
*
* @return FederatedShare[]
*/
public function getItemsFromRequest(CoreQueryBuilder $qb): array {
/** @var FederatedShare[] $result */
return $qb->asItems(FederatedShare::class);
}
}
@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Exceptions\ShareTokenNotFoundException;
use OCA\Circles\Model\ShareToken;
/**
* Class ShareTokenRequest
*
* @package OCA\Circles\Db
*/
class ShareTokenRequest extends ShareTokenRequestBuilder {
/**
* @param ShareToken $token
*
* @return void
*/
public function save(ShareToken $token): void {
$qb = $this->getTokenInsertSql();
$qb->setValue('share_id', $qb->createNamedParameter($token->getShareId()))
->setValue('circle_id', $qb->createNamedParameter($token->getCircleId()))
->setValue('single_id', $qb->createNamedParameter($token->getSingleId()))
->setValue('member_id', $qb->createNamedParameter($token->getMemberId()))
->setValue('token', $qb->createNamedParameter($token->getToken()))
->setValue('password', $qb->createNamedParameter($token->getPassword()))
->setValue('accepted', $qb->createNamedParameter($token->getAccepted()));
$qb->execute();
$id = $qb->getLastInsertId();
$token->setDbId($id);
}
/**
* @param ShareToken $shareToken
*
* @return ShareToken
* @throws ShareTokenNotFoundException
*/
public function search(ShareToken $shareToken): ShareToken {
$qb = $this->getTokenSelectSql();
$qb->limitInt('share_id', $shareToken->getshareId());
$qb->limitToCircleId($shareToken->getCircleId());
$qb->limitToSingleId($shareToken->getSingleId());
return $this->getItemFromRequest($qb);
}
/**
* @param string $token
*
* @return ShareToken
* @throws ShareTokenNotFoundException
*/
public function getByToken(string $token): ShareToken {
$qb = $this->getTokenSelectSql();
$qb->limitToToken($token);
return $this->getItemFromRequest($qb);
}
/**
* @param string $circleId
* @param string $hashedPassword
*/
public function updateSharePassword(string $circleId, string $hashedPassword) {
$qb = $this->getTokenUpdateSql();
$qb->limitToCircleId($circleId);
$qb->set('password', $qb->createNamedParameter($hashedPassword));
$qb->executeStatement();
}
/**
* @param string $singleId
* @param string $circleId
*/
public function removeTokens(string $singleId, string $circleId) {
$qb = $this->getTokenDeleteSql();
$qb->limitToSingleId($singleId);
$qb->limitToCircleId($circleId);
$qb->execute();
}
}
@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Exceptions\RowNotFoundException;
use OCA\Circles\Exceptions\ShareTokenNotFoundException;
use OCA\Circles\Model\ShareToken;
/**
* Class ShareTokenRequestBuilder
*
* @package OCA\Circles\Db
*/
class ShareTokenRequestBuilder extends CoreRequestBuilder {
/**
* @return CoreQueryBuilder
*/
protected function getTokenInsertSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->insert(self::TABLE_TOKEN);
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getTokenUpdateSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->update(self::TABLE_TOKEN);
return $qb;
}
/**
* @param string $alias
*
* @return CoreQueryBuilder
*/
protected function getTokenSelectSql(string $alias = CoreQueryBuilder::TOKEN): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->generateSelect(self::TABLE_TOKEN, self::$tables[self::TABLE_TOKEN], $alias);
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return CoreQueryBuilder
*/
protected function getTokenDeleteSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->delete(self::TABLE_TOKEN);
return $qb;
}
/**
* @param CoreQueryBuilder $qb
*
* @return ShareToken
* @throws ShareTokenNotFoundException
*/
public function getItemFromRequest(CoreQueryBuilder $qb): ShareToken {
/** @var ShareToken $shareToken */
try {
$shareToken = $qb->asItem(ShareToken::class);
} catch (RowNotFoundException $e) {
throw new ShareTokenNotFoundException();
}
return $shareToken;
}
/**
* @param CoreQueryBuilder $qb
*
* @return ShareToken[]
*/
public function getItemsFromRequest(CoreQueryBuilder $qb): array {
/** @var ShareToken[] $result */
return $qb->asItems(ShareToken::class);
}
}
@@ -0,0 +1,523 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use JsonException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\ShareWrapperNotFoundException;
use OCA\Circles\Model\FederatedUser;
use OCA\Circles\Model\Membership;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Model\ShareWrapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Folder;
use OCP\Files\NotFoundException;
use OCP\Share\Exceptions\IllegalIDChangeException;
use OCP\Share\IAttributes;
use OCP\Share\IShare;
/**
* Class ShareWrapperRequest
*
* @package OCA\Circles\Db
*/
class ShareWrapperRequest extends ShareWrapperRequestBuilder {
/**
* @param IShare $share
* @param int $parentId
*
* @return int
* @throws NotFoundException
*/
public function save(IShare $share, int $parentId = 0): int {
// $hasher = \OC::$server->getHasher();
// $password = ($share->getPassword() !== null) ? $hasher->hash($share->getPassword()) : '';
$password = '';
$qb = $this->getShareInsertSql();
$qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()))
->setValue('item_type', $qb->createNamedParameter($share->getNodeType()))
->setValue('item_source', $qb->createNamedParameter($share->getNodeId()))
->setValue('file_source', $qb->createNamedParameter($share->getNodeId()))
->setValue('file_target', $qb->createNamedParameter($share->getTarget()))
->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()))
->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED))
->setValue('password', $qb->createNamedParameter($password))
->setValue('permissions', $qb->createNamedParameter($share->getPermissions()))
->setValue('token', $qb->createNamedParameter($share->getToken()))
->setValue('stime', $qb->createFunction('UNIX_TIMESTAMP()'));
if ($parentId > 0) {
$qb->setValue('parent', $qb->createNamedParameter($parentId));
}
$qb->execute();
$id = $qb->getLastInsertId();
try {
$share->setId($id);
} catch (IllegalIDChangeException $e) {
}
return $id;
}
/**
* @param ShareWrapper $shareWrapper
*/
public function update(ShareWrapper $shareWrapper): void {
$qb = $this->getShareUpdateSql();
$shareAttributes = $this->formatShareAttributes($shareWrapper->getAttributes());
$qb->set('file_target', $qb->createNamedParameter($shareWrapper->getFileTarget()))
->set('share_with', $qb->createNamedParameter($shareWrapper->getSharedWith()))
->set('uid_owner', $qb->createNamedParameter($shareWrapper->getShareOwner()))
->set('uid_initiator', $qb->createNamedParameter($shareWrapper->getSharedBy()))
->set('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED))
->set('permissions', $qb->createNamedParameter($shareWrapper->getPermissions()))
->set('expiration', $qb->createNamedParameter($shareWrapper->getExpirationDate(), IQueryBuilder::PARAM_DATE))
->set('attributes', $qb->createNamedParameter($shareAttributes));
$qb->limitToId((int)$shareWrapper->getId());
$qb->execute();
}
/**
* @param Membership $membership
*/
public function deleteByMembership(Membership $membership) {
$qb = $this->getShareDeleteSql();
$qb->limitToShareWith($membership->getCircleId());
$qb->limit('uid_initiator', $membership->getSingleId());
$qb->execute();
}
/**
* @return array
*/
public function getShares(): array {
$qb = $this->getShareSelectSql();
return $this->getItemsFromRequest($qb);
}
/**
* @param string $circleId
* @param FederatedUser|null $shareRecipient
* @param FederatedUser|null $shareInitiator
* @param bool $completeDetails
*
* @return ShareWrapper[]
* @throws RequestBuilderException
*/
public function getSharesToCircle(
string $circleId,
?FederatedUser $shareRecipient = null,
?FederatedUser $shareInitiator = null,
bool $completeDetails = false
): array {
$qb = $this->getShareSelectSql();
$qb->limitNull('parent', false);
$qb->setOptions([CoreQueryBuilder::SHARE], ['getData' => true]);
$qb->leftJoinCircle(CoreQueryBuilder::SHARE, null, 'share_with');
// TODO: filter direct-shares ?
$aliasUpstreamMembership =
$qb->generateAlias(CoreQueryBuilder::SHARE, CoreQueryBuilder::UPSTREAM_MEMBERSHIPS);
$qb->limitToInheritedMemberships(CoreQueryBuilder::SHARE, $circleId, 'share_with');
// if (!is_null($shareRecipient)) {
// $qb->limitToInitiator(CoreRequestBuilder::SHARE, $shareRecipient, 'share_with');
// }
// TODO: add shareInitiator and shareRecipient to filter the request
if (!is_null($shareRecipient) || $completeDetails) {
$qb->leftJoinInheritedMembers(
$aliasUpstreamMembership,
'circle_id',
$qb->generateAlias(CoreQueryBuilder::SHARE, CoreQueryBuilder::INHERITED_BY)
);
$aliasMembership = $qb->generateAlias($aliasUpstreamMembership, CoreQueryBuilder::MEMBERSHIPS);
$qb->leftJoinFileCache(CoreQueryBuilder::SHARE);
$qb->leftJoinShareChild(CoreQueryBuilder::SHARE, $aliasMembership);
}
return $this->getItemsFromRequest($qb);
}
/**
* @param int $shareId
* @param FederatedUser|null $federatedUser
*
* @return ShareWrapper
* @throws ShareWrapperNotFoundException
* @throws RequestBuilderException
*/
public function getShareById(int $shareId, ?FederatedUser $federatedUser = null): ShareWrapper {
$qb = $this->getShareSelectSql();
$qb->setOptions([CoreQueryBuilder::SHARE], ['getData' => true]);
$qb->leftJoinCircle(CoreQueryBuilder::SHARE, null, 'share_with');
$qb->limitToId($shareId);
if (!is_null($federatedUser)) {
$qb->limitToInitiator(CoreQueryBuilder::SHARE, $federatedUser, 'share_with');
$qb->leftJoinShareChild(CoreQueryBuilder::SHARE);
}
return $this->getItemFromRequest($qb);
}
/**
* @param string $token
* @param FederatedUser|null $federatedUser
*
* @return ShareWrapper
* @throws RequestBuilderException
* @throws ShareWrapperNotFoundException
*/
public function getShareByToken(string $token, ?FederatedUser $federatedUser = null): ShareWrapper {
$qb = $this->getShareSelectSql();
$qb->setOptions([CoreQueryBuilder::SHARE], ['getData' => true]);
$qb->leftJoinCircle(CoreQueryBuilder::SHARE, null, 'share_with');
$qb->limitToShareToken(CoreQueryBuilder::SHARE, $token);
if (!is_null($federatedUser)) {
$qb->limitToInitiator(CoreQueryBuilder::SHARE, $federatedUser, 'share_with');
$qb->leftJoinShareChild(CoreQueryBuilder::SHARE);
}
return $this->getItemFromRequest($qb);
}
/**
* @param FederatedUser $federatedUser
* @param int $shareId
*
* @return ShareWrapper
* @throws ShareWrapperNotFoundException
*/
public function getChild(FederatedUser $federatedUser, int $shareId): ShareWrapper {
$qb = $this->getShareSelectSql();
$qb->limitToShareParent($shareId);
$qb->limitToShareWith($federatedUser->getSingleId());
return $this->getItemFromRequest($qb);
}
/**
* @param int $fileId
* @param bool $getData
*
* @return ShareWrapper[]
* @throws RequestBuilderException
*/
public function getSharesByFileId(int $fileId, bool $getData = false): array {
$qb = $this->getShareSelectSql();
$qb->limitToFileSource($fileId);
if ($getData) {
$qb->setOptions([CoreQueryBuilder::SHARE], ['getData' => $getData]);
$qb->leftJoinCircle(CoreQueryBuilder::SHARE, null, 'share_with');
// $qb->leftJoinFileCache(CoreRequestBuilder::SHARE);
$qb->limitNull('parent', false);
$aliasMembership = $qb->generateAlias(CoreQueryBuilder::SHARE, CoreQueryBuilder::MEMBERSHIPS);
$qb->leftJoinInheritedMembers(CoreQueryBuilder::SHARE, 'share_with');
$qb->leftJoinShareChild(CoreQueryBuilder::SHARE);
}
return $this->getItemsFromRequest($qb);
}
/**
* @param FederatedUser $federatedUser
* @param int $nodeId
* @param int $offset
* @param int $limit
* @param bool $getData
*
* @return ShareWrapper[]
* @throws RequestBuilderException
*/
public function getSharedWith(
FederatedUser $federatedUser,
int $nodeId,
CircleProbe $probe
): array {
$qb = $this->getShareSelectSql();
$qb->setOptions(
[CoreQueryBuilder::SHARE],
array_merge(
$probe->getAsOptions(),
['getData' => true]
)
);
$qb->leftJoinCircle(CoreQueryBuilder::SHARE, null, 'share_with');
$aliasCircle = $qb->generateAlias(CoreQueryBuilder::SHARE, CoreQueryBuilder::CIRCLE);
$qb->limitToFederatedUserMemberships(CoreQueryBuilder::SHARE, $aliasCircle, $federatedUser);
$qb->leftJoinFileCache(CoreQueryBuilder::SHARE);
$qb->limitNull('parent', false);
$qb->leftJoinShareChild(CoreQueryBuilder::SHARE);
if ($nodeId > 0) {
$qb->limitToFileSource($nodeId);
}
$qb->chunk($probe->getItemsOffset(), $probe->getItemsLimit());
return $this->getItemsFromRequest($qb);
}
/**
* @param FederatedUser $federatedUser
* @param int $nodeId
* @param bool $reshares
* @param int $offset
* @param int $limit
* @param bool $getData
* @param bool $completeDetails
*
* @return ShareWrapper[]
* @throws RequestBuilderException
*/
public function getSharesBy(
FederatedUser $federatedUser,
int $nodeId,
bool $reshares,
int $limit,
int $offset,
bool $getData = false,
bool $completeDetails = false
): array {
$qb = $this->getShareSelectSql();
$qb->setOptions([CoreQueryBuilder::SHARE], ['getData' => $getData]);
$qb->leftJoinCircle(CoreQueryBuilder::SHARE, null, 'share_with');
$qb->limitToShareOwner(CoreQueryBuilder::SHARE, $federatedUser, $reshares);
$qb->limitNull('parent', false);
if ($nodeId > 0) {
$qb->limitToFileSource($nodeId);
}
if ($completeDetails) {
$aliasMembership = $qb->generateAlias(CoreQueryBuilder::SHARE, CoreQueryBuilder::MEMBERSHIPS);
$qb->leftJoinInheritedMembers(CoreQueryBuilder::SHARE, 'share_with');
$qb->leftJoinFileCache(CoreQueryBuilder::SHARE);
$qb->leftJoinShareChild(CoreQueryBuilder::SHARE, $aliasMembership);
}
$qb->chunk($offset, $limit);
return $this->getItemsFromRequest($qb);
}
/**
* @param FederatedUser $federatedUser
* @param Folder $node
* @param bool $reshares
* @param bool $shallow Whether the method should stop at the first level, or look into sub-folders.
*
* @return ShareWrapper[]
* @throws RequestBuilderException
*/
public function getSharesInFolder(
FederatedUser $federatedUser,
Folder $node,
bool $reshares,
bool $shallow = true
): array {
$qb = $this->getShareSelectSql();
$qb->leftJoinCircle(CoreQueryBuilder::SHARE, null, 'share_with');
$qb->limitToShareOwner(CoreQueryBuilder::SHARE, $federatedUser, $reshares);
$qb->leftJoinFileCache(CoreQueryBuilder::SHARE);
$aliasFileCache = $qb->generateAlias(CoreQueryBuilder::SHARE, CoreQueryBuilder::FILE_CACHE);
if ($shallow) {
$qb->limitInt('parent', $node->getId(), $aliasFileCache);
} else {
$qb->like('path', $node->getInternalPath() . '/%', $aliasFileCache);
}
$qb->limitNull('parent', false);
return $this->getItemsFromRequest($qb);
}
/**
* returns the SQL request to get a specific share from the fileId and circleId
*
* @param string $singleId
* @param int $fileId
*
* @return ShareWrapper
* @throws ShareWrapperNotFoundException
* @throws RequestBuilderException
*/
public function searchShare(string $singleId, int $fileId): ShareWrapper {
$qb = $this->getShareSelectSql();
$qb->setOptions([CoreQueryBuilder::SHARE], ['getData' => true]);
$qb->leftJoinCircle(CoreQueryBuilder::SHARE, null, 'share_with');
$qb->limitNull('parent', false);
$qb->limitToShareWith($singleId);
$qb->limitToFileSource($fileId);
return $this->getItemFromRequest($qb);
}
/**
* @param int $shareId
*/
public function delete(int $shareId): void {
$qb = $this->getShareDeleteSql();
$qb->andWhere(
$qb->expr()->orX(
$qb->exprLimitInt('id', $shareId),
$qb->exprLimitInt('parent', $shareId),
)
);
$qb->execute();
}
/**
* @param string $circleId
* @param string $initiator
*/
public function deleteSharesToCircle(string $circleId, string $initiator = ''): void {
$qb = $this->getShareSelectSql();
$qb->limit('share_with', $circleId);
if ($initiator !== '') {
$qb->limit('uid_initiator', $initiator);
}
$ids = array_map(
function (ShareWrapper $share): string {
return $share->getId();
},
$this->getItemsFromRequest($qb)
);
$this->deleteSharesAndChild($ids);
}
public function removeOrphanShares(): void {
$qb = $this->getShareSelectSql();
$expr = $qb->expr();
$qb->leftJoin(
CoreQueryBuilder::SHARE, CoreRequestBuilder::TABLE_SHARE, 'p',
$expr->andX($expr->eq('p.id', CoreQueryBuilder::SHARE . '.parent'))
);
$qb->filterNull('parent');
$qb->limitNull('id', false, 'p');
$ids = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$ids[] = $data['id'];
}
$cursor->closeCursor();
$this->deleteSharesAndChild($ids);
}
/**
* @param array $ids
*/
private function deleteSharesAndChild(array $ids): void {
$qb = $this->getShareDeleteSql();
$qb->andWhere(
$qb->expr()->orX(
$qb->exprLimitInArray('id', $ids),
$qb->exprLimitInArray('parent', $ids)
)
);
$qb->execute();
}
/**
* Format IAttributes to database format (JSON string)
* based on OC\Share20\DefaultShareProvider::formatShareAttributes();
*/
private function formatShareAttributes(?IAttributes $attributes): ?string {
if (empty($attributes?->toArray())) {
return null;
}
$compressedAttributes = [];
foreach ($attributes->toArray() as $attribute) {
$compressedAttributes[] = [
$attribute['scope'],
$attribute['key'],
$attribute['enabled']
];
}
try {
return json_encode($compressedAttributes, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
return null;
}
}
}
@@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Tools\Exceptions\RowNotFoundException;
use OCP\Share\IShare;
use OCA\Circles\Exceptions\ShareWrapperNotFoundException;
use OCA\Circles\Model\ShareWrapper;
/**
* Class ShareWrapperRequestBuilder
*
* @package OCA\Circles\Db
*/
class ShareWrapperRequestBuilder extends CoreRequestBuilder {
/**
* @return CoreQueryBuilder
*/
protected function getShareInsertSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->insert(self::TABLE_SHARE);
return $qb;
}
/**
* @return CoreQueryBuilder
*/
protected function getShareUpdateSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->update(self::TABLE_SHARE);
return $qb;
}
/**
* @param string $alias
*
* @return CoreQueryBuilder
*/
protected function getShareSelectSql(string $alias = CoreQueryBuilder::SHARE): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->generateSelect(self::TABLE_SHARE, self::$outsideTables[self::TABLE_SHARE], $alias)
->limitToShareType(IShare::TYPE_CIRCLE);
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return CoreQueryBuilder
*/
protected function getShareDeleteSql(): CoreQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->delete(self::TABLE_SHARE)
->limitToShareType(IShare::TYPE_CIRCLE);
return $qb;
}
/**
* @param CoreQueryBuilder $qb
*
* @return ShareWrapper
* @throws ShareWrapperNotFoundException
*/
public function getItemFromRequest(CoreQueryBuilder $qb): ShareWrapper {
/** @var ShareWrapper $shareWrapper */
try {
$shareWrapper = $qb->asItem(ShareWrapper::class);
} catch (RowNotFoundException $e) {
throw new ShareWrapperNotFoundException();
}
return $shareWrapper;
}
/**
* @param CoreQueryBuilder $qb
*
* @return ShareWrapper[]
*/
public function getItemsFromRequest(CoreQueryBuilder $qb): array {
/** @var ShareWrapper[] $result */
return $qb->asItems(ShareWrapper::class);
}
}
@@ -0,0 +1,178 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OCA\Circles\Exceptions\TokenDoesNotExistException;
use OCA\Circles\Model\DeprecatedMember;
use OCA\Circles\Model\SharesToken;
/**
* @deprecated
* Class TokensRequest
*
* @package OCA\Circles\Db
*/
class TokensRequest extends TokensRequestBuilder {
/**
* @param string $token
*
* @return SharesToken
* @throws TokenDoesNotExistException
*/
public function getByToken(string $token) {
$qb = $this->getTokensSelectSql();
$this->limitToToken($qb, $token);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new TokenDoesNotExistException('Unknown share token');
}
return $this->parseTokensSelectSql($data);
}
/**
* @param string $shareId
* @param string $circleId
* @param string $email
*
* @return SharesToken
* @throws TokenDoesNotExistException
*/
public function getTokenFromMember(string $shareId, string $circleId, string $email) {
$qb = $this->getTokensSelectSql();
$this->limitToShareId($qb, $shareId);
$this->limitToUserId($qb, $email);
$this->limitToCircleId($qb, $circleId);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new TokenDoesNotExistException('Unknown share token');
}
return $this->parseTokensSelectSql($data);
}
/**
* @param DeprecatedMember $member
*
* @return SharesToken[]
*/
public function getTokensFromMember(DeprecatedMember $member) {
$qb = $this->getTokensSelectSql();
$this->limitToUserId($qb, $member->getUserId());
$this->limitToCircleId($qb, $member->getCircleId());
$shares = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$shares[] = $this->parseTokensSelectSql($data);
}
$cursor->closeCursor();
return $shares;
}
/**
* @param DeprecatedMember $member
* @param int $shareId
* @param string $password
*
* @return SharesToken
* @throws TokenDoesNotExistException
*/
public function generateTokenForMember(DeprecatedMember $member, int $shareId, string $password = ''): SharesToken {
try {
$token = $this->miscService->token(15);
if ($password !== '') {
$hasher = \OC::$server->getHasher();
$password = $hasher->hash($password);
}
$qb = $this->getTokensInsertSql();
$qb->setValue('circle_id', $qb->createNamedParameter($member->getCircleId()))
->setValue('user_id', $qb->createNamedParameter($member->getUserId()))
->setValue('share_id', $qb->createNamedParameter($shareId))
->setValue('member_id', $qb->createNamedParameter($member->getMemberId()))
->setValue('token', $qb->createNamedParameter($token))
->setValue('password', $qb->createNamedParameter($password));
$qb->execute();
} catch (UniqueConstraintViolationException $e) {
}
return $this->getTokenFromMember($shareId, $member->getCircleId(), $member->getUserId());
}
/**
* @param int $shareId
*/
public function removeTokenByShareId(int $shareId) {
$qb = $this->getTokensDeleteSql();
$this->limitToShareId($qb, $shareId);
$qb->execute();
}
/**
* @param DeprecatedMember $member
*/
public function removeTokensFromMember(DeprecatedMember $member) {
$qb = $this->getTokensDeleteSql();
$this->limitToCircleId($qb, $member->getCircleId());
$this->limitToUserId($qb, $member->getUserId());
$qb->execute();
}
public function updateSinglePassword(string $circleId, string $password) {
$qb = $this->getTokensUpdateSql();
if ($password !== '') {
$hasher = \OC::$server->getHasher();
$password = $hasher->hash($password);
}
$this->limitToCircleId($qb, $circleId);
$qb->set('password', $qb->createNamedParameter($password));
$qb->execute();
}
}
@@ -0,0 +1,111 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2017
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Db;
use OCA\Circles\Model\SharesToken;
use OCA\Circles\Tools\Traits\TArrayTools;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* @deprecated
* Class TokensRequestBuilder
*
* @package OCA\Circles\Db
*/
class TokensRequestBuilder extends DeprecatedRequestBuilder {
use TArrayTools;
/**
* Base of the Sql Insert request for Shares
*
* @return IQueryBuilder
*/
protected function getTokensInsertSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::TABLE_TOKENS);
return $qb;
}
/**
* Base of the Sql Update request for Groups
*
* @return IQueryBuilder
*/
protected function getTokensUpdateSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->update(self::TABLE_TOKENS);
return $qb;
}
/**
* @return IQueryBuilder
*/
protected function getTokensSelectSql() {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select('t.user_id', 't.circle_id', 't.member_id', 't.share_id', 't.token', 't.accepted')
->from(self::TABLE_TOKENS, 't');
$this->default_select_alias = 't';
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return IQueryBuilder
*/
protected function getTokensDeleteSql() {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::TABLE_TOKENS);
return $qb;
}
/**
* @param array $data
*
* @return SharesToken
*/
protected function parseTokensSelectSql($data) {
$sharesToken = new SharesToken();
$sharesToken->import($data);
return $sharesToken;
}
}
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Federated\FederatedEvent;
/**
* Class AddingCircleMemberEvent
*
* This event is called when one or multiple members are added to a Circle.
* This event is called on every instance of Nextcloud related to the Circle.
*
* The entry is already generated in the members table.
* The memberships of the member[s] are already generated in the memberships table.
*
* This is a good place if anything needs to be executed when a new member have been added to a Circle.
*
* If anything needs to be managed on the master instance of the Circle (ie. CircleMemberAddedEvent), please use:
* $event->getFederatedEvent()->setResultEntry(string $key, array $data);
*
* @package OCA\Circles\Events
*/
class AddingCircleMemberEvent extends CircleMemberGenericEvent {
/** @var int */
private $type = 0;
/**
* AddingCircleMemberEvent constructor.
*
* @param FederatedEvent $federatedEvent
*/
public function __construct(FederatedEvent $federatedEvent) {
parent::__construct($federatedEvent);
}
/**
* @param int $type
*
* @return $this
*/
public function setType(int $type): self {
$this->type = $type;
return $this;
}
/**
* @return int
*/
public function getType(): int {
return $this->type;
}
}
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Federated\FederatedEvent;
/**
* Class CircleCreatedEvent
*
* This Event is called when it has been confirmed that the Circle have been created on all targeted
* instances.
* Targeted instance for this event are usually the one configured as GlobalScale.
*
* Meaning that the event won't be triggered until each instances have been once available during the
* retry-on-fail initiated in a background job
*
* WARNING: Unlike CreatingCircleEvent, this Event is only called on the master instance of the Circle.
*
* @package OCA\Circles\Events
*/
class CircleCreatedEvent extends CircleResultGenericEvent {
/**
* CircleCreatedEvent constructor.
*
* @param FederatedEvent $federatedEvent
* @param array $results
*/
public function __construct(FederatedEvent $federatedEvent, array $results) {
parent::__construct($federatedEvent, $results);
}
}
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Federated\FederatedEvent;
/**
* Class CircleDestroyedEvent
*
* This Event is called when it has been confirmed that the Circle have been destroyed on all instances
* related to the Circle.
*
* Meaning that the event won't be triggered until each instances have been once available during the
* retry-on-fail initiated in a background job
*
* WARNING: Unlike DestroyingCircleEvent, this Event is only called on the master instance of the Circle.
*
* @package OCA\Circles\Events
*/
class CircleDestroyedEvent extends CircleResultGenericEvent {
/**
* CircleDestroyedEvent constructor.
*
* @param FederatedEvent $federatedEvent
* @param array $results
*/
public function __construct(FederatedEvent $federatedEvent, array $results) {
parent::__construct($federatedEvent, $results);
}
}
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Federated\FederatedEvent;
/**
* Class CircleEditedEvent
*
* This Event is called when it has been confirmed that the Circle have been edited on all targeted
* instances.
*
* Meaning that the event won't be triggered until each instances have been once available during the
* retry-on-fail initiated in a background job
*
* WARNING: Unlike EditingCircleEvent, this Event is only called on the master instance of the Circle.
*
* @package OCA\Circles\Events
*/
class CircleEditedEvent extends CircleResultGenericEvent {
/**
* CircleEditedEvent constructor.
*
* @param FederatedEvent $federatedEvent
* @param array $results
*/
public function __construct(FederatedEvent $federatedEvent, array $results) {
parent::__construct($federatedEvent, $results);
}
}
@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Federated\FederatedEvent;
use OCP\EventDispatcher\Event;
/**
* Class CircleMemberAddedEvent
*
* @package OCA\Circles\Events
*/
class CircleGenericEvent extends Event {
public const INVITED = 1;
public const JOINED = 2;
/** @deprecated */
public const MULTIPLE = 3;
public const REMOVED = 4;
public const LEFT = 5;
public const LEVEL = 6;
public const NAME = 7;
public const REQUESTED = 8;
/** @var FederatedEvent */
private $federatedEvent;
/** @var Circle */
private $circle;
/**
* CircleMemberAddedEvent constructor.
*
* @param FederatedEvent $federatedEvent
*/
public function __construct(FederatedEvent $federatedEvent) {
parent::__construct();
$this->federatedEvent = $federatedEvent;
$this->circle = $federatedEvent->getCircle();
}
/**
* @return FederatedEvent
*/
public function getFederatedEvent(): FederatedEvent {
return $this->federatedEvent;
}
/**
* @return Circle
*/
public function getCircle(): Circle {
return $this->circle;
}
}
@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Federated\FederatedEvent;
/**
* Class CircleMemberAddedEvent
*
* This Event is called when it has been confirmed that a Member have been added on all instances used
* by the Circle.
* Meaning that the event won't be triggered until each instances have been once available during the
* retry-on-fail initiated in a background job
*
* WARNING: Unlike AddingCircleMemberEvent, this Event is only called on the master instance of the Circle.
*
* @package OCA\Circles\Events
*/
class CircleMemberAddedEvent extends CircleResultGenericEvent {
/** @var int */
private $type = 0;
/**
* CircleMemberAddedEvent constructor.
*
* @param FederatedEvent $federatedEvent
* @param array $results
*/
public function __construct(FederatedEvent $federatedEvent, array $results) {
parent::__construct($federatedEvent, $results);
}
/**
* @param int $type
*
* @return $this
*/
public function setType(int $type): self {
$this->type = $type;
return $this;
}
/**
* @return int
*/
public function getType(): int {
return $this->type;
}
}
@@ -0,0 +1,124 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Federated\FederatedEvent;
/**
* Class CircleMemberEditedEvent
*
* This Event is called when it has been confirmed that a Member have been edited on all instances used
* by the Circle.
* Meaning that the event won't be triggered until each instances have been once available during the
* retry-on-fail initiated in a background job
*
* WARNING: Unlike EditingCircleMemberEvent, this Event is only called on the master instance of the Circle.
*
* @package OCA\Circles\Events
*/
class CircleMemberEditedEvent extends CircleResultGenericEvent {
/** @var int */
private $type = 0;
/** @var int */
private $newLevel = 0;
/** @var string */
private $newDisplayName = '';
/**
* CircleMemberEditedEvent constructor.
*
* @param FederatedEvent $federatedEvent
* @param array $results
*/
public function __construct(FederatedEvent $federatedEvent, array $results) {
parent::__construct($federatedEvent, $results);
}
/**
* @param int $type
*
* @return $this
*/
public function setType(int $type): self {
$this->type = $type;
return $this;
}
/**
* @return int
*/
public function getType(): int {
return $this->type;
}
/**
* @param int $newLevel
*
* @return CircleMemberEditedEvent
*/
public function setNewLevel(int $newLevel): self {
$this->newLevel = $newLevel;
return $this;
}
/**
* @return int
*/
public function getNewLevel(): int {
return $this->newLevel;
}
/**
* @param string $newDisplayName
*
* @return CircleMemberEditedEvent
*/
public function setNewDisplayName(string $newDisplayName): self {
$this->newDisplayName = $newDisplayName;
return $this;
}
/**
* @return string
*/
public function getNewDisplayName(): string {
return $this->newDisplayName;
}
}
@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Federated\FederatedEvent;
use OCA\Circles\Model\Member;
/**
* Class CircleMemberAddedEvent
*
* @package OCA\Circles\Events
*/
class CircleMemberGenericEvent extends CircleGenericEvent {
/** @var Member */
private $member;
/**
* CircleMemberAddedEvent constructor.
*
* @param FederatedEvent $federatedEvent
*/
public function __construct(FederatedEvent $federatedEvent) {
parent::__construct($federatedEvent);
$this->member = $federatedEvent->getMember();
}
/**
* @return Member
*/
public function getMember(): Member {
return $this->member;
}
}
@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Federated\FederatedEvent;
/**
* Class CircleMemberRemovedEvent
*
* This Event is called when it has been confirmed that a Member have been removed on all instances used
* by the Circle.
* Meaning that the event won't be triggered until each instances have been once available during the
* retry-on-fail initiated in a background job
*
* WARNING: Unlike RemovingCircleMemberEvent, this Event is only called on the master instance of the Circle.
*
* @package OCA\Circles\Events
*/
class CircleMemberRemovedEvent extends CircleResultGenericEvent {
/** @var int */
private $type = 0;
/**
* CircleMemberRemovedEvent constructor.
*
* @param FederatedEvent $federatedEvent
* @param array $results
*/
public function __construct(FederatedEvent $federatedEvent, array $results) {
parent::__construct($federatedEvent, $results);
}
/**
* @param int $type
*
* @return $this
*/
public function setType(int $type): self {
$this->type = $type;
return $this;
}
/**
* @return int
*/
public function getType(): int {
return $this->type;
}
}
@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Federated\FederatedEvent;
/**
* Class CircleMemberRequestedEvent
*
* This Event is called when it has been confirmed that a Member's request/invitation is done on all instances
* used by the Circle.
* Meaning that the event won't be triggered until each instances have been once available during the
* retry-on-fail initiated in a background job
*
* WARNING: Unlike RequestingCircleMemberEvent, this Event is only called on the master instance of the Circle.
*
* @package OCA\Circles\Events
*/
class CircleMemberRequestedEvent extends CircleResultGenericEvent {
/** @var int */
private $type = 0;
/**
* CircleMemberRequestedEvent constructor.
*
* @param FederatedEvent $federatedEvent
* @param array $results
*/
public function __construct(FederatedEvent $federatedEvent, array $results) {
parent::__construct($federatedEvent, $results);
}
/**
* @param int $type
*
* @return $this
*/
public function setType(int $type): self {
$this->type = $type;
return $this;
}
/**
* @return int
*/
public function getType(): int {
return $this->type;
}
}
@@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Tools\Model\SimpleDataStore;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Federated\FederatedEvent;
use OCA\Circles\Model\Member;
use OCP\EventDispatcher\Event;
/**
* Class CircleResultGenericEvent
*
* @package OCA\Circles\Events
*/
class CircleResultGenericEvent extends Event {
/** @var FederatedEvent */
private $federatedEvent;
/** @var SimpleDataStore[] */
private $results;
/** @var Circle */
private $circle;
/** @var Member */
private $member;
/**
* CircleResultGenericEvent constructor.
*
* @param FederatedEvent $federatedEvent
* @param SimpleDataStore[] $results
*/
public function __construct(FederatedEvent $federatedEvent, array $results) {
parent::__construct();
$this->federatedEvent = $federatedEvent;
$this->results = $results;
$this->circle = $federatedEvent->getCircle();
if ($federatedEvent->hasMember()) {
$this->member = $federatedEvent->getMember();
}
}
/**
* @return FederatedEvent
*/
public function getFederatedEvent(): FederatedEvent {
return $this->federatedEvent;
}
/**
* @return SimpleDataStore[]
*/
public function getResults(): array {
return $this->results;
}
/**
* @return Circle
*/
public function getCircle(): Circle {
return $this->circle;
}
/**
* @return bool
*/
public function hasMember(): bool {
return (!is_null($this->member));
}
/**
* @return Member|null
*/
public function getMember(): ?Member {
return $this->member;
}
}
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Federated\FederatedEvent;
/**
* Class CreatingCircleEvent
*
* This event is called when a new Circle is created.
* This event is called on every targeted instance of Nextcloud.
* Targeted instance for this event are usually the one configured as GlobalScale.
*
* The entry is already generated in the circles table.
* The owner is already generated in the members table.
*
* This is a good place if anything needs to be executed when a new Circle has been created.
*
* If anything needs to be managed on the master instance of the Circle (ie. CircleCreatedEvent), please use:
* $event->getFederatedEvent()->setResultEntry(string $key, array $data);
*
* @package OCA\Circles\Events
*/
class CreatingCircleEvent extends CircleGenericEvent {
/**
* CreatingCircleEvent constructor.
*
* @param FederatedEvent $federatedEvent
*/
public function __construct(FederatedEvent $federatedEvent) {
parent::__construct($federatedEvent);
}
}
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Federated\FederatedEvent;
/**
* Class DestroyingCircleEvent
*
* This event is called when a Circle is destroyed.
* This event is called on every instance of Nextcloud related to the Circle.
*
* The entry is already removed from the circles table.
* The members are already removed from the members table.
* The entries from the memberships table are already refreshed.
*
* This is a good place if anything needs to be executed when a Circle has been destroyed.
*
* If anything needs to be managed on the master instance of the Circle (ie. CircleDestroyedEvent), please use:
* $event->getFederatedEvent()->setResultEntry(string $key, array $data);
* *
* @package OCA\Circles\Events
*/
class DestroyingCircleEvent extends CircleGenericEvent {
/**
* DestroyingCircleEvent constructor.
*
* @param FederatedEvent $federatedEvent
*/
public function __construct(FederatedEvent $federatedEvent) {
parent::__construct($federatedEvent);
}
}
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Events;
use OCA\Circles\Model\Federated\FederatedEvent;
/**
* Class EditingCircleEvent
*
* This event is called when a circle is edited.
* This event is called on every instance of Nextcloud related to the Circle.
*
* The entry is already edited in the circles table.
* If needed, the entries in the memberships table are already edited.
*
* This is a good place if anything needs to be executed when a circle is edited.
*
* If anything needs to be managed on the master instance of the Circle (ie. CircleEditedEvent), please use:
* $event->getFederatedEvent()->setResultEntry(string $key, array $data);
*
* @package OCA\Circles\Events
*/
class EditingCircleEvent extends CircleGenericEvent {
/**
* EditingCircleEvent constructor.
*
* @param FederatedEvent $federatedEvent
*/
public function __construct(FederatedEvent $federatedEvent) {
parent::__construct($federatedEvent);
}
}

Some files were not shown because too many files have changed in this diff Show More