diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index b5298554c..58aac0e8a 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -41,6 +41,7 @@
use OCA\Circles\Events\CircleMemberAddedEvent;
use OCA\Circles\Events\MembershipsCreatedEvent;
use OCA\Circles\Events\MembershipsRemovedEvent;
+use OCA\Circles\Events\RemovingCircleMemberEvent;
use OCA\Circles\Events\RequestingCircleMemberEvent;
use OCA\Circles\Handlers\WebfingerHandler;
use OCA\Circles\Listeners\DeprecatedListener;
@@ -50,7 +51,7 @@
use OCA\Circles\Listeners\Examples\ExampleRequestingCircleMember;
use OCA\Circles\Listeners\Files\AddingMember as ListenerFilesAddingMember;
use OCA\Circles\Listeners\Files\MemberAdded as ListenerFilesMemberAdded;
-use OCA\Circles\Listeners\Files\MembershipsRemoved as ListenerFilesMembershipsRemoved;
+use OCA\Circles\Listeners\Files\RemovingMember as ListenerFilesRemovingMember;
use OCA\Circles\Listeners\GroupCreated;
use OCA\Circles\Listeners\GroupDeleted;
use OCA\Circles\Listeners\GroupMemberAdded;
@@ -62,13 +63,11 @@
use OCA\Circles\Notification\Notifier;
use OCA\Circles\Service\ConfigService;
use OCA\Circles\Service\DavService;
-//use OCA\Files\App as FilesApp;
use OCP\App\ManagerEvent;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
-use OCP\AppFramework\IAppContainer;
use OCP\Files\Config\IMountProviderCollection;
use OCP\Group\Events\GroupCreatedEvent;
use OCP\Group\Events\GroupDeletedEvent;
@@ -81,6 +80,8 @@
use Symfony\Component\EventDispatcher\GenericEvent;
use Throwable;
+//use OCA\Files\App as FilesApp;
+
require_once __DIR__ . '/../../vendor/autoload.php';
@@ -107,9 +108,6 @@ class Application extends App implements IBootstrap {
/** @var ConfigService */
private $configService;
- /** @var IAppContainer */
- private $container;
-
/**
* Application constructor.
@@ -143,9 +141,7 @@ public function register(IRegistrationContext $context): void {
// Local Events (for Files/Shares/Notifications management)
$context->registerEventListener(AddingCircleMemberEvent::class, ListenerFilesAddingMember::class);
$context->registerEventListener(CircleMemberAddedEvent::class, ListenerFilesMemberAdded::class);
- $context->registerEventListener(
- MembershipsRemovedEvent::class, ListenerFilesMembershipsRemoved::class
- );
+ $context->registerEventListener(RemovingCircleMemberEvent::class, ListenerFilesRemovingMember::class);
$context->registerEventListener(
RequestingCircleMemberEvent::class, ListenerNotificationsRequestingMember::class
);
diff --git a/lib/Command/CirclesMemberships.php b/lib/Command/CirclesMemberships.php
index 14e164f6f..39e729159 100644
--- a/lib/Command/CirclesMemberships.php
+++ b/lib/Command/CirclesMemberships.php
@@ -162,7 +162,7 @@ protected function configure() {
->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('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',
@@ -208,11 +208,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$type = Member::parseTypeString($input->getOption('type'));
$federatedUser = $this->federatedUserService->getFederatedUser($userId, (int)$type);
- if ($this->input->getOption('reset')) {
- $this->membershipsService->resetMemberships($federatedUser->getSingleId());
-
- return 0;
- }
+// if ($this->input->getOption('reset')) {
+// $this->membershipsService->resetMemberships($federatedUser->getSingleId());
+//
+// return 0;
+// }
$output->writeln('Id: ' . $federatedUser->getUserId() . '');
$output->writeln('Instance: ' . $federatedUser->getInstance() . '');
@@ -361,11 +361,11 @@ public function displayLeaf(SimpleDataStore $data, int $lineNumber): string {
* @throws RequestBuilderException
*/
private function manageAllMemberships() {
- if ($this->input->getOption('reset')) {
- $this->membershipsService->resetMemberships('', true);
-
- return;
- }
+// if ($this->input->getOption('reset')) {
+// $this->membershipsService->resetMemberships('', true);
+//
+// return;
+// }
$this->federatedUserService->bypassCurrentUserCondition(true);
diff --git a/lib/Db/ShareTokenRequest.php b/lib/Db/ShareTokenRequest.php
index e8030747f..ccf3048a5 100644
--- a/lib/Db/ShareTokenRequest.php
+++ b/lib/Db/ShareTokenRequest.php
@@ -65,6 +65,22 @@ public function save(ShareToken $token): void {
}
+ /**
+ * @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
*
@@ -78,5 +94,18 @@ public function getByToken(string $token): ShareToken {
return $this->getItemFromRequest($qb);
}
+
+ /**
+ * @param string $singleId
+ * @param string $circleId
+ */
+ public function removeTokens(string $singleId, string $circleId) {
+ $qb = $this->getTokenDeleteSql();
+ $qb->limitToSingleId($singleId);
+ $qb->limitToCircleId($circleId);
+
+ $qb->execute();
+ }
+
}
diff --git a/lib/Db/ShareWrapperRequest.php b/lib/Db/ShareWrapperRequest.php
index aaa7f2870..70bf6ec89 100644
--- a/lib/Db/ShareWrapperRequest.php
+++ b/lib/Db/ShareWrapperRequest.php
@@ -168,6 +168,7 @@ public function getSharesToCircle(
$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');
@@ -176,20 +177,18 @@ public function getSharesToCircle(
// $qb->limitToInitiator(CoreRequestBuilder::SHARE, $shareRecipient, 'share_with');
// }
- $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);
+ // 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)
+ );
- if (!is_null($shareInitiator)) {
- }
+ $aliasMembership = $qb->generateAlias($aliasUpstreamMembership, CoreQueryBuilder::MEMBERSHIPS);
+ $qb->leftJoinFileCache(CoreQueryBuilder::SHARE);
+ $qb->leftJoinShareChild(CoreQueryBuilder::SHARE, $aliasMembership);
- if ($completeDetails) {
$qb->generateGroupBy(
self::$tables[self::TABLE_MEMBERSHIP],
$aliasMembership,
diff --git a/lib/Events/AddingCircleMemberEvent.php b/lib/Events/AddingCircleMemberEvent.php
index 75c704092..1aeb05a9a 100644
--- a/lib/Events/AddingCircleMemberEvent.php
+++ b/lib/Events/AddingCircleMemberEvent.php
@@ -47,7 +47,7 @@
* 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()->addResult(string $key, array $data);
+ * $event->getFederatedEvent()->setResultEntry(string $key, array $data);
*
* @package OCA\Circles\Events
*/
diff --git a/lib/Events/CreatingCircleEvent.php b/lib/Events/CreatingCircleEvent.php
index 1702a2a89..7d2a1003e 100644
--- a/lib/Events/CreatingCircleEvent.php
+++ b/lib/Events/CreatingCircleEvent.php
@@ -48,7 +48,7 @@
* 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()->addResult(string $key, array $data);
+ * $event->getFederatedEvent()->setResultEntry(string $key, array $data);
*
* @package OCA\Circles\Events
*/
diff --git a/lib/Events/DestroyingCircleEvent.php b/lib/Events/DestroyingCircleEvent.php
index ed1395401..d096dda2f 100644
--- a/lib/Events/DestroyingCircleEvent.php
+++ b/lib/Events/DestroyingCircleEvent.php
@@ -48,7 +48,7 @@
* 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()->addResult(string $key, array $data);
+ * $event->getFederatedEvent()->setResultEntry(string $key, array $data);
* *
* @package OCA\Circles\Events
*/
diff --git a/lib/Events/EditingCircleEvent.php b/lib/Events/EditingCircleEvent.php
index d8c2ab76a..2280c0cdc 100644
--- a/lib/Events/EditingCircleEvent.php
+++ b/lib/Events/EditingCircleEvent.php
@@ -47,7 +47,7 @@
* 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()->addResult(string $key, array $data);
+ * $event->getFederatedEvent()->setResultEntry(string $key, array $data);
*
* @package OCA\Circles\Events
*/
diff --git a/lib/Events/EditingCircleMemberEvent.php b/lib/Events/EditingCircleMemberEvent.php
index ac2c250b7..5deb30d8f 100644
--- a/lib/Events/EditingCircleMemberEvent.php
+++ b/lib/Events/EditingCircleMemberEvent.php
@@ -48,7 +48,7 @@
*
* If anything needs to be managed on the master instance of the Circle (ie. CircleMemberEditedEvent), please
* use:
- * $event->getFederatedEvent()->addResult(string $key, array $data);
+ * $event->getFederatedEvent()->setResultEntry(string $key, array $data);
*
* @package OCA\Circles\Events
*/
diff --git a/lib/Events/RemovingCircleMemberEvent.php b/lib/Events/RemovingCircleMemberEvent.php
index 0619ae94c..e6b273f4d 100644
--- a/lib/Events/RemovingCircleMemberEvent.php
+++ b/lib/Events/RemovingCircleMemberEvent.php
@@ -47,7 +47,7 @@
* This is a good place if anything needs to be executed when a member have been removed from a Circle.
*
* If anything needs to be managed on the master instance of the Circle (ie. CircleMemberRemovedEvent), please use:
- * $event->getFederatedEvent()->addResult(string $key, array $data);
+ * $event->getFederatedEvent()->setResultEntry(string $key, array $data);
*
* @package OCA\Circles\Events
*/
diff --git a/lib/Events/RequestingCircleMemberEvent.php b/lib/Events/RequestingCircleMemberEvent.php
index fae4d8aa7..75233cb1e 100644
--- a/lib/Events/RequestingCircleMemberEvent.php
+++ b/lib/Events/RequestingCircleMemberEvent.php
@@ -46,7 +46,7 @@
* This is a good place if anything needs to be executed when a member requests or is invited to a Circle.
*
* If anything needs to be managed on the master instance of the Circle (ie. CircleMemberRequestedEvent), please use:
- * $event->getFederatedEvent()->addResult(string $key, array $data);
+ * $event->getFederatedEvent()->setResultEntry(string $key, array $data);
*
* @package OCA\Circles\Events
*/
diff --git a/lib/Exceptions/ShareTokenAlreadyExistException.php b/lib/Exceptions/ShareTokenAlreadyExistException.php
new file mode 100644
index 000000000..70597bfcc
--- /dev/null
+++ b/lib/Exceptions/ShareTokenAlreadyExistException.php
@@ -0,0 +1,45 @@
+
+ * @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 .
+ *
+ */
+
+namespace OCA\Circles\Exceptions;
+
+
+use Exception;
+
+
+/**
+ * Class ShareTokenAlreadyExistException
+ *
+ * @package OCA\Circles\Exceptions
+ */
+class ShareTokenAlreadyExistException extends Exception {
+
+}
+
diff --git a/lib/FederatedItems/SingleMemberAdd.php b/lib/FederatedItems/SingleMemberAdd.php
index 467044624..dcd6a7f3d 100644
--- a/lib/FederatedItems/SingleMemberAdd.php
+++ b/lib/FederatedItems/SingleMemberAdd.php
@@ -633,6 +633,7 @@ private function memberIsMailbox(
}
try {
+
$template = $this->generateMailExitingShares($author, $circle->getName());
$this->fillMailExistingShares($template, $links);
$this->sendMailExistingShares($template, $author, $recipient);
diff --git a/lib/Listeners/Files/AddingMember.php b/lib/Listeners/Files/AddingMember.php
index 1874ee4dd..92ec83a4b 100644
--- a/lib/Listeners/Files/AddingMember.php
+++ b/lib/Listeners/Files/AddingMember.php
@@ -32,10 +32,17 @@
namespace OCA\Circles\Listeners\Files;
+use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Logger;
use ArtificialOwl\MySmallPhpTools\Traits\TStringTools;
+use Exception;
+use OCA\Circles\AppInfo\Application;
use OCA\Circles\Events\AddingCircleMemberEvent;
use OCA\Circles\Exceptions\RequestBuilderException;
+use OCA\Circles\Exceptions\ShareTokenAlreadyExistException;
use OCA\Circles\Model\Member;
+use OCA\Circles\Service\ConfigService;
+use OCA\Circles\Service\ContactService;
+use OCA\Circles\Service\ShareTokenService;
use OCA\Circles\Service\ShareWrapperService;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
@@ -50,19 +57,42 @@ class AddingMember implements IEventListener {
use TStringTools;
+ use TNC22Logger;
/** @var ShareWrapperService */
private $shareWrapperService;
+ /** @var ShareTokenService */
+ private $shareTokenService;
+
+ /** @var ConfigService */
+ private $configService;
+
+ /** @var ContactService */
+ private $contactService;
+
/**
* AddingMember constructor.
*
* @param ShareWrapperService $shareWrapperService
+ * @param ShareTokenService $shareTokenService
+ * @param ContactService $contactService
+ * @param ConfigService $configService
*/
- public function __construct(ShareWrapperService $shareWrapperService) {
+ public function __construct(
+ ShareWrapperService $shareWrapperService,
+ ShareTokenService $shareTokenService,
+ ContactService $contactService,
+ ConfigService $configService
+ ) {
$this->shareWrapperService = $shareWrapperService;
+ $this->shareTokenService = $shareTokenService;
+ $this->contactService = $contactService;
+ $this->configService = $configService;
+
+ $this->setup('app', Application::APP_ID);
}
@@ -76,20 +106,64 @@ public function handle(Event $event): void {
return;
}
- $bypass = true;
+ $result = [];
$member = $event->getMember();
- if ($member->getUserType() === Member::TYPE_MAIL) {
- $bypass = false;
- }
- if ($bypass) {
- return;
+ if ($member->getUserType() === Member::TYPE_CIRCLE) {
+ $members = $member->getBasedOn()->getInheritedMembers();
+ } else {
+ $members = [$member];
}
$circle = $event->getCircle();
- $files = $this->shareWrapperService->getSharesToCircle($circle->getSingleId());
+ $shares = $this->shareWrapperService->getSharesToCircle($circle->getSingleId());
+
+ /** @var Member[] $members */
+ foreach ($members as $member) {
+ if ($member->getUserType() !== Member::TYPE_MAIL
+ && $member->getUserType() !== Member::TYPE_CONTACT
+ ) {
+ continue;
+ }
+
+ $files = [];
+ foreach ($shares as $share) {
+ try {
+ $shareToken = $this->shareTokenService->generateShareToken($share, $member);
+ } catch (ShareTokenAlreadyExistException $e) {
+ continue;
+ }
+
+ $share->setShareToken($shareToken);
+ $files[] = $share;
+ }
+
+ $result[$member->getId()] = [
+ 'shares' => $files,
+ 'mails' => $this->getMailAddressesFromContact($member)
+ ];
+ }
+
+ $event->getFederatedEvent()->setResultEntry('files', $result);
+ }
+
+
+ /**
+ * @param Member $member
+ *
+ * @return array
+ */
+ private function getMailAddressesFromContact(Member $member): array {
+ if ($member->getUserType() !== Member::TYPE_CONTACT
+ || !$this->configService->isLocalInstance($member->getInstance())) {
+ return [];
+ }
- $event->getFederatedEvent()->addResult('files', $files);
+ try {
+ return $this->contactService->getMailAddresses($member->getUserId());
+ } catch (Exception $e) {
+ return [];
+ }
}
}
diff --git a/lib/Listeners/Files/MemberAdded.php b/lib/Listeners/Files/MemberAdded.php
index 146f379af..385202aa4 100644
--- a/lib/Listeners/Files/MemberAdded.php
+++ b/lib/Listeners/Files/MemberAdded.php
@@ -32,11 +32,23 @@
namespace OCA\Circles\Listeners\Files;
+use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Logger;
use ArtificialOwl\MySmallPhpTools\Traits\TStringTools;
+use Exception;
+use OCA\Circles\AppInfo\Application;
use OCA\Circles\Events\CircleMemberAddedEvent;
+use OCA\Circles\Exceptions\RequestBuilderException;
+use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Member;
+use OCA\Circles\Model\ShareWrapper;
+use OCA\Circles\Service\ShareWrapperService;
+use OCP\Defaults;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
+use OCP\IL10N;
+use OCP\Mail\IEMailTemplate;
+use OCP\Mail\IMailer;
+use OCP\Util;
/**
@@ -48,33 +60,193 @@ class MemberAdded implements IEventListener {
use TStringTools;
+ use TNC22Logger;
+
+
+ /** @var IL10N */
+ private $l10n;
+
+ /** @var IMailer */
+ private $mailer;
+
+ /** @var Defaults */
+ private $defaults;
+
+ /** @var ShareWrapperService */
+ private $shareWrapperService;
+
+
+ /**
+ * MemberAdded constructor.
+ *
+ * @param IL10N $l10n
+ * @param IMailer $mailer
+ * @param Defaults $defaults
+ * @param ShareWrapperService $shareWrapperService
+ */
+ public function __construct(
+ IL10N $l10n,
+ IMailer $mailer,
+ Defaults $defaults,
+ ShareWrapperService $shareWrapperService
+ ) {
+ $this->l10n = $l10n;
+ $this->mailer = $mailer;
+ $this->defaults = $defaults;
+ $this->shareWrapperService = $shareWrapperService;
+
+ $this->setup('app', Application::APP_ID);
+ }
/**
* @param Event $event
+ *
+ * @throws RequestBuilderException
*/
public function handle(Event $event): void {
if (!$event instanceof CircleMemberAddedEvent) {
return;
}
- $federatedUsers = [];
$member = $event->getMember();
+ $circle = $event->getCircle();
+
+ if ($member->getUserType() === Member::TYPE_CIRCLE) {
+ $members = $member->getBasedOn()->getInheritedMembers();
+ } else {
+ $members = [$member];
+ }
+
+ /** @var Member[] $members */
+ foreach ($members as $member) {
+ if ($member->getUserType() !== Member::TYPE_MAIL
+ && $member->getUserType() !== Member::TYPE_CONTACT
+ ) {
+ continue;
+ }
+
+ $mails = [];
+ $shares = [];
+ foreach ($event->getResults() as $origin => $item) {
+ $files = $item->gData('files');
+ if (!$files->hasKey($member->getId())) {
+ continue;
+ }
+
+ $data = $files->gData($member->getId());
+ $shares = array_merge($shares, $data->gObjs('shares', ShareWrapper::class));
+
+ // TODO: is it safe to use $origin to compare getInstance() ?
+ if ($member->getUserType() === Member::TYPE_CONTACT && $member->getInstance() === $origin) {
+ $mails = $data->gArray('mails');
+ }
+ }
+
+ $this->generateMail($circle, $member, $shares, $mails);
+ }
+ }
+
+
+ /**
+ * @param Circle $circle
+ * @param Member $member
+ * @param ShareWrapper[] $shares
+ * @param array $mails
+ */
+ private function generateMail(Circle $circle, Member $member, array $shares, array $mails): void {
+ if (empty($shares)) {
+ return;
+ }
+
if ($member->getUserType() === Member::TYPE_MAIL) {
- $federatedUsers[] = $member;
+ $mails = [$member->getUserId()];
}
- if (empty($federatedUsers)) {
+ if (empty($mails)) {
return;
}
- $result = [];
- foreach ($event->getResults() as $instance => $item) {
- $result[$instance] = $item->gData('files');
+ if ($member->hasInvitedBy()) {
+ $invitedBy = $member->getInvitedBy()->getDisplayName();
+ } else {
+ $invitedBy = 'someone';
+ }
+
+ $links = [];
+ foreach ($shares as $share) {
+ $links[] = [
+ 'filename' => $share->getFileTarget(),
+ 'link' => $share->getShareToken()->getLink()
+ ];
+ }
+
+ $template = $this->generateMailExitingShares($invitedBy, $circle->getDisplayName());
+ $this->fillMailExistingShares($template, $links);
+ foreach ($mails as $mail) {
+ try {
+ $this->sendMailExistingShares($template, $invitedBy, $mail);
+ } catch (Exception $e) {
+ }
}
+ }
+
+
+ /**
+ * @param string $author
+ * @param string $circleName
+ *
+ * @return IEMailTemplate
+ */
+ private function generateMailExitingShares(string $author, string $circleName): IEMailTemplate {
+ $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 $links
+ */
+ private function fillMailExistingShares(IEMailTemplate $emailTemplate, array $links) {
+ foreach ($links as $item) {
+ $emailTemplate->addBodyButton(
+ $this->l10n->t('Open »%s«', [htmlspecialchars($item['filename'])]), $item['link']
+ );
+ }
+ }
+
+
+ /**
+ * @param IEMailTemplate $emailTemplate
+ * @param string $author
+ * @param string $recipient
+ *
+ * @throws Exception
+ */
+ private function sendMailExistingShares(
+ IEMailTemplate $emailTemplate,
+ string $author,
+ string $recipient
+ ) {
+ $subject = $this->l10n->t('%s shared multiple files with you.', [$author]);
+
+ $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]);
- \OC::$server->getLogger()->log(3, 'FILES: ' . json_encode($result));
- \OC::$server->getLogger()->log(3, 'MAILS: ' . json_encode($federatedUsers));
+ $this->mailer->send($message);
}
}
diff --git a/lib/Listeners/Files/RemovingMember.php b/lib/Listeners/Files/RemovingMember.php
new file mode 100644
index 000000000..6e34a0e5f
--- /dev/null
+++ b/lib/Listeners/Files/RemovingMember.php
@@ -0,0 +1,131 @@
+
+ * @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 .
+ *
+ */
+
+
+namespace OCA\Circles\Listeners\Files;
+
+
+use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Logger;
+use ArtificialOwl\MySmallPhpTools\Traits\TStringTools;
+use OCA\Circles\AppInfo\Application;
+use OCA\Circles\Events\RemovingCircleMemberEvent;
+use OCA\Circles\Exceptions\MembershipNotFoundException;
+use OCA\Circles\Model\Member;
+use OCA\Circles\Model\Membership;
+use OCA\Circles\Service\MemberService;
+use OCA\Circles\Service\ShareTokenService;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+
+
+/**
+ * Class RemovingMember
+ *
+ * @package OCA\Circles\Listeners\Files
+ */
+class RemovingMember implements IEventListener {
+
+
+ use TStringTools;
+ use TNC22Logger;
+
+
+ /** @var MemberService */
+ private $memberService;
+
+ /** @var ShareTokenService */
+ private $shareTokenService;
+
+
+ /**
+ * RemovingMember constructor.
+ *
+ * @param MemberService $memberService
+ * @param ShareTokenService $shareTokenService
+ */
+ public function __construct(
+ MemberService $memberService,
+ ShareTokenService $shareTokenService
+ ) {
+ $this->memberService = $memberService;
+ $this->shareTokenService = $shareTokenService;
+
+ $this->setup('app', Application::APP_ID);
+ }
+
+
+ /**
+ * @param Event $event
+ */
+ public function handle(Event $event): void {
+ if (!$event instanceof RemovingCircleMemberEvent) {
+ return;
+ }
+
+ $member = $event->getMember();
+
+ if ($member->getUserType() === Member::TYPE_CIRCLE) {
+ $members = $member->getBasedOn()->getInheritedMembers();
+ } else {
+ $members = [$member];
+ }
+
+ $circle = $event->getCircle();
+ $singleIds = array_merge(
+ [$circle->getSingleId()],
+ array_map(
+ function(Membership $membership) {
+ return $membership->getCircleId();
+ }, $circle->getMemberships()
+ )
+ );
+
+ /** @var Member[] $members */
+ foreach ($members as $member) {
+ if ($member->getUserType() !== Member::TYPE_MAIL
+ && $member->getUserType() !== Member::TYPE_CONTACT
+ ) {
+ continue;
+ }
+
+ foreach ($singleIds as $singleId) {
+ try {
+ $member->getMembership($singleId);
+ continue;
+ } catch (MembershipNotFoundException $e) {
+ }
+
+ $this->shareTokenService->removeTokens($member->getSingleId(), $singleId);
+ }
+ }
+ }
+
+}
+
diff --git a/lib/Model/Federated/FederatedEvent.php b/lib/Model/Federated/FederatedEvent.php
index 4187b568f..441b7bc5a 100644
--- a/lib/Model/Federated/FederatedEvent.php
+++ b/lib/Model/Federated/FederatedEvent.php
@@ -494,7 +494,23 @@ public function resetResult(): self {
*
* @return $this
*/
- public function addResult(string $key, array $result): self {
+ public function setResultEntry(string $key, array $result): self {
+ if (is_null($this->result)) {
+ $this->result = new SimpleDataStore();
+ }
+
+ $this->result->sData($key, new SimpleDataStore($result));
+
+ return $this;
+ }
+
+ /**
+ * @param string $key
+ * @param array $result
+ *
+ * @return $this
+ */
+ public function addResultEntry(string $key, array $result): self {
if (is_null($this->result)) {
$this->result = new SimpleDataStore();
}
diff --git a/lib/Model/Member.php b/lib/Model/Member.php
index 27b9003cf..92f4287a3 100644
--- a/lib/Model/Member.php
+++ b/lib/Model/Member.php
@@ -40,6 +40,7 @@
use JsonSerializable;
use OCA\Circles\AppInfo\Capabilities;
use OCA\Circles\Exceptions\MemberNotFoundException;
+use OCA\Circles\Exceptions\MembershipNotFoundException;
use OCA\Circles\Exceptions\ParseMemberLevelException;
use OCA\Circles\Exceptions\UnknownInterfaceException;
use OCA\Circles\Exceptions\UserTypeNotFoundException;
@@ -726,6 +727,22 @@ public function getMemberships(): array {
return $this->memberships;
}
+ /**
+ * @param string $circleId
+ *
+ * @return Membership
+ * @throws MembershipNotFoundException
+ */
+ public function getMembership(string $circleId): Membership {
+ foreach ($this->getMemberships() as $membership) {
+ if ($membership->getCircleId() === $circleId) {
+ return $membership;
+ }
+ }
+
+ throw new MembershipNotFoundException();
+ }
+
/**
* @param Member $member
diff --git a/lib/Model/ShareToken.php b/lib/Model/ShareToken.php
index a25b993eb..47af80517 100644
--- a/lib/Model/ShareToken.php
+++ b/lib/Model/ShareToken.php
@@ -33,6 +33,7 @@
use ArtificialOwl\MySmallPhpTools\Db\Nextcloud\nc22\INC22QueryRow;
+use ArtificialOwl\MySmallPhpTools\Exceptions\InvalidItemException;
use ArtificialOwl\MySmallPhpTools\IDeserializable;
use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
use JsonSerializable;
@@ -69,6 +70,9 @@ class ShareToken implements IDeserializable, INC22QueryRow, JsonSerializable {
/** @var int */
private $accepted = IShare::STATUS_PENDING;
+ /** @var string */
+ private $link = '';
+
/**
* ShareToken constructor.
@@ -229,12 +233,36 @@ public function getAccepted(): int {
}
+ /**
+ * @param string $link
+ *
+ * @return ShareToken
+ */
+ public function setLink(string $link): self {
+ $this->link = $link;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLink(): string {
+ return $this->link;
+ }
+
+
/**
* @param array $data
*
* @return ShareToken
+ * @throws InvalidItemException
*/
public function import(array $data): IDeserializable {
+ if ($this->getInt('shareId', $data) === 0) {
+ throw new InvalidItemException();
+ }
+
$this->setShareId($this->getInt('shareId', $data));
$this->setCircleId($this->get('circleId', $data));
$this->setSingleId($this->get('singleId', $data));
@@ -242,6 +270,7 @@ public function import(array $data): IDeserializable {
$this->setToken($this->get('token', $data));
$this->setPassword($this->get('password', $data));
$this->setAccepted($this->getInt('accepted', $data, IShare::STATUS_PENDING));
+ $this->setLink($this->get('link', $data));
return $this;
}
@@ -276,7 +305,8 @@ function jsonSerialize(): array {
'memberId' => $this->getMemberId(),
'token' => $this->getToken(),
'password' => $this->getPassword(),
- 'accepted' => $this->getAccepted()
+ 'accepted' => $this->getAccepted(),
+ 'link' => $this->getLink()
];
}
diff --git a/lib/Model/ShareWrapper.php b/lib/Model/ShareWrapper.php
index 4b3f0a38b..bb7cbd2d6 100644
--- a/lib/Model/ShareWrapper.php
+++ b/lib/Model/ShareWrapper.php
@@ -128,6 +128,9 @@ class ShareWrapper extends ManagedModel implements IDeserializable, INC22QueryRo
/** @var Member */
private $owner;
+ /** @var ShareToken */
+ private $shareToken;
+
/**
* @param string $id
@@ -574,6 +577,32 @@ public function hasOwner(): bool {
}
+ /**
+ * @param ShareToken $shareToken
+ *
+ * @return ShareWrapper
+ */
+ public function setShareToken(ShareToken $shareToken): self {
+ $this->shareToken = $shareToken;
+
+ return $this;
+ }
+
+ /**
+ * @return ShareToken
+ */
+ public function getShareToken(): ShareToken {
+ return $this->shareToken;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasShareToken(): bool {
+ return !is_null($this->shareToken);
+ }
+
+
/**
* @param IRootFolder $rootFolder
* @param IUserManager $userManager
@@ -680,7 +709,17 @@ private function setShareDisplay(IShare $share, IURLGenerator $urlGenerator) {
}
+ /**
+ * @param array $data
+ *
+ * @return IDeserializable
+ * @throws InvalidItemException
+ */
public function import(array $data): IDeserializable {
+ if ($this->getInt('id', $data) === 0) {
+ throw new InvalidItemException();
+ }
+
$shareTime = new DateTime();
$shareTime->setTimestamp($this->getInt('shareTime', $data));
@@ -728,6 +767,12 @@ public function import(array $data): IDeserializable {
} catch (InvalidItemException $e) {
}
+ try {
+ $shareToken = new ShareToken();
+ $this->setShareToken($shareToken->import($this->getArray('shareToken', $data)));
+ } catch (InvalidItemException $e) {
+ }
+
return $this;
}
@@ -815,6 +860,10 @@ public function jsonSerialize(): array {
$arr['fileCache'] = $this->getFileCache();
}
+ if ($this->hasShareToken()) {
+ $arr['shareToken'] = $this->getShareToken();
+ }
+
return $arr;
}
diff --git a/lib/Service/MembershipService.php b/lib/Service/MembershipService.php
index cd7400eb4..69650f6f3 100644
--- a/lib/Service/MembershipService.php
+++ b/lib/Service/MembershipService.php
@@ -159,6 +159,7 @@ public function manageAll(): void {
* @param string $singleId
*
* @return int
+ * @throws RequestBuilderException
*/
public function manageMemberships(string $singleId): int {
$memberships = $this->generateMemberships($singleId);
diff --git a/lib/Service/MigrationService.php b/lib/Service/MigrationService.php
index 47e50bb7c..e18f82a0d 100644
--- a/lib/Service/MigrationService.php
+++ b/lib/Service/MigrationService.php
@@ -218,10 +218,11 @@ private function migrationTo22(): void {
$this->migrationTo22_Circles();
$this->migrationTo22_Members();
- $this->migrationTo22_Tokens();
$this->membershipService->resetMemberships('', true);
$this->membershipService->manageAll();
+ $this->migrationTo22_Tokens();
+
$this->configService->setAppValue(ConfigService::MIGRATION_22, '1');
}
@@ -404,7 +405,6 @@ private function generateMemberFrom21(SimpleDataStore $data): Member {
// "cached_update":"2021-05-02 12:13:22",
// "joined":"2021-05-02 12:13:22",
// "contact_checked":null,"
-// single_id":"wt6WQYYCry3EOud",
// "circle_source":null}
return $member;
@@ -525,6 +525,11 @@ private function generateShareTokenFrom21(SimpleDataStore $data): ShareToken {
$shareToken = new ShareToken();
$member = $this->memberRequest->getMemberById($data->g('member_id'));
+ if ($member->getUserType() !== Member::TYPE_MAIL
+ && $member->getUserType() !== Member::TYPE_CONTACT) {
+ throw new MemberNotFoundException();
+ }
+
$shareToken->setShareId($data->gInt('share_id'))
->setCircleId($data->g('circle_id'))
->setSingleId($member->getSingleId())
diff --git a/lib/Service/ShareTokenService.php b/lib/Service/ShareTokenService.php
new file mode 100644
index 000000000..c5e61e622
--- /dev/null
+++ b/lib/Service/ShareTokenService.php
@@ -0,0 +1,138 @@
+
+ * @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 .
+ *
+ */
+
+
+namespace OCA\Circles\Service;
+
+
+use ArtificialOwl\MySmallPhpTools\Traits\TStringTools;
+use OCA\Circles\Db\ShareTokenRequest;
+use OCA\Circles\Exceptions\ShareTokenAlreadyExistException;
+use OCA\Circles\Exceptions\ShareTokenNotFoundException;
+use OCA\Circles\Model\Member;
+use OCA\Circles\Model\ShareToken;
+use OCA\Circles\Model\ShareWrapper;
+use OCP\IURLGenerator;
+use OCP\Share\IShare;
+
+
+/**
+ * Class ShareTokenService
+ *
+ * @package OCA\Circles\Service
+ */
+class ShareTokenService {
+
+
+ use TStringTools;
+
+
+ /** @var IURLGenerator */
+ private $urlGenerator;
+
+ /** @var ShareTokenRequest */
+ private $shareTokenRequest;
+
+
+ /**
+ * ShareTokenService constructor.
+ *
+ * @param IURLGenerator $urlGenerator
+ * @param ShareTokenRequest $shareTokenRequest
+ */
+ public function __construct(
+ IURLGenerator $urlGenerator,
+ ShareTokenRequest $shareTokenRequest
+ ) {
+ $this->urlGenerator = $urlGenerator;
+ $this->shareTokenRequest = $shareTokenRequest;
+ }
+
+
+ /**
+ * @param ShareWrapper $share
+ * @param Member $member
+ * @param string $password
+ *
+ * @return ShareToken
+ * @throws ShareTokenAlreadyExistException
+ */
+ public function generateShareToken(
+ ShareWrapper $share,
+ Member $member,
+ string $password = ''
+ ): ShareToken {
+ $token = $this->token(19);
+
+ $shareToken = new ShareToken();
+ $shareToken->setShareId((int)$share->getId())
+ ->setCircleId($share->getSharedWith())
+ ->setSingleId($member->getSingleId())
+ ->setMemberId($member->getId())
+ ->setToken($token)
+ ->setPassword($password)
+ ->setAccepted(IShare::STATUS_ACCEPTED);
+
+ try {
+ $this->shareTokenRequest->search($shareToken);
+ throw new ShareTokenAlreadyExistException();
+ } catch (ShareTokenNotFoundException $e) {
+ }
+
+ $this->shareTokenRequest->save($shareToken);
+ $this->setShareTokenLink($shareToken);
+
+ return $shareToken;
+ }
+
+
+ /**
+ * @param ShareToken $shareToken
+ */
+ public function setShareTokenLink(ShareToken $shareToken): void {
+ $link = $this->urlGenerator->linkToRouteAbsolute(
+ 'files_sharing.sharecontroller.showShare',
+ ['token' => $shareToken->getToken()]
+ );
+
+ $shareToken->setLink($link);
+ }
+
+
+ /**
+ * @param string $singleId
+ * @param string $circleId
+ */
+ public function removeTokens(string $singleId, string $circleId) {
+ $this->shareTokenRequest->removeTokens($singleId, $circleId);
+ }
+
+}
+
diff --git a/lib/Service/ShareWrapperService.php b/lib/Service/ShareWrapperService.php
index 12767a145..a9540d20f 100644
--- a/lib/Service/ShareWrapperService.php
+++ b/lib/Service/ShareWrapperService.php
@@ -32,6 +32,7 @@
namespace OCA\Circles\Service;
+use ArtificialOwl\MySmallPhpTools\Traits\TStringTools;
use OCA\Circles\Db\ShareWrapperRequest;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\ShareWrapperNotFoundException;
@@ -49,6 +50,9 @@
class ShareWrapperService {
+ use TStringTools;
+
+
/** @var ShareWrapperRequest */
private $shareWrapperRequest;
@@ -64,10 +68,11 @@ public function __construct(ShareWrapperRequest $shareWrapperRequest) {
/**
- * @param $singleId
- * @param $nodeId
+ * @param string $singleId
+ * @param int $nodeId
*
* @return ShareWrapper
+ * @throws RequestBuilderException
* @throws ShareWrapperNotFoundException
*/
public function searchShare(string $singleId, int $nodeId): ShareWrapper {
@@ -140,6 +145,7 @@ public function getShareById(int $shareId, ?FederatedUser $federatedUser = null)
/**
* @param int $fileId
+ * @param bool $getData
*
* @return ShareWrapper[]
* @throws RequestBuilderException