Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/files_sharing/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,15 @@
'OCA\\Files_Sharing\\Listener\\BeforeDirectFileDownloadListener' => $baseDir . '/../lib/Listener/BeforeDirectFileDownloadListener.php',
'OCA\\Files_Sharing\\Listener\\BeforeNodeReadListener' => $baseDir . '/../lib/Listener/BeforeNodeReadListener.php',
'OCA\\Files_Sharing\\Listener\\BeforeZipCreatedListener' => $baseDir . '/../lib/Listener/BeforeZipCreatedListener.php',
'OCA\\Files_Sharing\\Listener\\CircleListenerBase' => $baseDir . '/../lib/Listener/CircleListenerBase.php',
'OCA\\Files_Sharing\\Listener\\LoadAdditionalListener' => $baseDir . '/../lib/Listener/LoadAdditionalListener.php',
'OCA\\Files_Sharing\\Listener\\LoadPublicFileRequestAuthListener' => $baseDir . '/../lib/Listener/LoadPublicFileRequestAuthListener.php',
'OCA\\Files_Sharing\\Listener\\LoadSidebarListener' => $baseDir . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files_Sharing\\Listener\\MemberAddedToCircleListener' => $baseDir . '/../lib/Listener/MemberAddedToCircleListener.php',
'OCA\\Files_Sharing\\Listener\\MemberRemovedFromCircleListener' => $baseDir . '/../lib/Listener/MemberRemovedFromCircleListener.php',
'OCA\\Files_Sharing\\Listener\\ShareInteractionListener' => $baseDir . '/../lib/Listener/ShareInteractionListener.php',
'OCA\\Files_Sharing\\Listener\\UserAddedToGroupListener' => $baseDir . '/../lib/Listener/UserAddedToGroupListener.php',
'OCA\\Files_Sharing\\Listener\\UserRemovedFromGroupListener' => $baseDir . '/../lib/Listener/UserRemovedFromGroupListener.php',
'OCA\\Files_Sharing\\Listener\\UserShareAcceptanceListener' => $baseDir . '/../lib/Listener/UserShareAcceptanceListener.php',
'OCA\\Files_Sharing\\Middleware\\OCSShareAPIMiddleware' => $baseDir . '/../lib/Middleware/OCSShareAPIMiddleware.php',
'OCA\\Files_Sharing\\Middleware\\ShareInfoMiddleware' => $baseDir . '/../lib/Middleware/ShareInfoMiddleware.php',
Expand Down
4 changes: 4 additions & 0 deletions apps/files_sharing/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,15 @@ class ComposerStaticInitFiles_Sharing
'OCA\\Files_Sharing\\Listener\\BeforeDirectFileDownloadListener' => __DIR__ . '/..' . '/../lib/Listener/BeforeDirectFileDownloadListener.php',
'OCA\\Files_Sharing\\Listener\\BeforeNodeReadListener' => __DIR__ . '/..' . '/../lib/Listener/BeforeNodeReadListener.php',
'OCA\\Files_Sharing\\Listener\\BeforeZipCreatedListener' => __DIR__ . '/..' . '/../lib/Listener/BeforeZipCreatedListener.php',
'OCA\\Files_Sharing\\Listener\\CircleListenerBase' => __DIR__ . '/..' . '/../lib/Listener/CircleListenerBase.php',
'OCA\\Files_Sharing\\Listener\\LoadAdditionalListener' => __DIR__ . '/..' . '/../lib/Listener/LoadAdditionalListener.php',
'OCA\\Files_Sharing\\Listener\\LoadPublicFileRequestAuthListener' => __DIR__ . '/..' . '/../lib/Listener/LoadPublicFileRequestAuthListener.php',
'OCA\\Files_Sharing\\Listener\\LoadSidebarListener' => __DIR__ . '/..' . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files_Sharing\\Listener\\MemberAddedToCircleListener' => __DIR__ . '/..' . '/../lib/Listener/MemberAddedToCircleListener.php',
'OCA\\Files_Sharing\\Listener\\MemberRemovedFromCircleListener' => __DIR__ . '/..' . '/../lib/Listener/MemberRemovedFromCircleListener.php',
'OCA\\Files_Sharing\\Listener\\ShareInteractionListener' => __DIR__ . '/..' . '/../lib/Listener/ShareInteractionListener.php',
'OCA\\Files_Sharing\\Listener\\UserAddedToGroupListener' => __DIR__ . '/..' . '/../lib/Listener/UserAddedToGroupListener.php',
'OCA\\Files_Sharing\\Listener\\UserRemovedFromGroupListener' => __DIR__ . '/..' . '/../lib/Listener/UserRemovedFromGroupListener.php',
'OCA\\Files_Sharing\\Listener\\UserShareAcceptanceListener' => __DIR__ . '/..' . '/../lib/Listener/UserShareAcceptanceListener.php',
'OCA\\Files_Sharing\\Middleware\\OCSShareAPIMiddleware' => __DIR__ . '/..' . '/../lib/Middleware/OCSShareAPIMiddleware.php',
'OCA\\Files_Sharing\\Middleware\\ShareInfoMiddleware' => __DIR__ . '/..' . '/../lib/Middleware/ShareInfoMiddleware.php',
Expand Down
9 changes: 9 additions & 0 deletions apps/files_sharing/lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use OC\Group\DisplayNameCache as GroupDisplayNameCache;
use OC\Share\Share;
use OC\User\DisplayNameCache;
use OCA\Circles\Events\CircleMemberAddedEvent;
use OCA\Circles\Events\CircleMemberRemovedEvent;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\Files\Event\LoadSidebar;
use OCA\Files_Sharing\Capabilities;
Expand All @@ -23,8 +25,11 @@
use OCA\Files_Sharing\Listener\LoadAdditionalListener;
use OCA\Files_Sharing\Listener\LoadPublicFileRequestAuthListener;
use OCA\Files_Sharing\Listener\LoadSidebarListener;
use OCA\Files_Sharing\Listener\MemberAddedToCircleListener;
use OCA\Files_Sharing\Listener\MemberRemovedFromCircleListener;
use OCA\Files_Sharing\Listener\ShareInteractionListener;
use OCA\Files_Sharing\Listener\UserAddedToGroupListener;
use OCA\Files_Sharing\Listener\UserRemovedFromGroupListener;
use OCA\Files_Sharing\Listener\UserShareAcceptanceListener;
use OCA\Files_Sharing\Middleware\OCSShareAPIMiddleware;
use OCA\Files_Sharing\Middleware\ShareInfoMiddleware;
Expand All @@ -49,6 +54,7 @@
use OCP\Group\Events\GroupChangedEvent;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\Group\Events\UserAddedEvent;
use OCP\Group\Events\UserRemovedEvent;
use OCP\IDBConnection;
use OCP\IGroup;
use OCP\Share\Events\ShareCreatedEvent;
Expand Down Expand Up @@ -97,6 +103,9 @@
$context->registerEventListener(ShareCreatedEvent::class, ShareInteractionListener::class);
$context->registerEventListener(ShareCreatedEvent::class, UserShareAcceptanceListener::class);
$context->registerEventListener(UserAddedEvent::class, UserAddedToGroupListener::class);
$context->registerEventListener(UserRemovedEvent::class, UserRemovedFromGroupListener::class);
$context->registerEventListener(CircleMemberAddedEvent::class, MemberAddedToCircleListener::class);

Check failure on line 107 in apps/files_sharing/lib/AppInfo/Application.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidArgument

apps/files_sharing/lib/AppInfo/Application.php:107:66: InvalidArgument: Argument 2 of OCP\AppFramework\Bootstrap\IRegistrationContext::registerEventListener expects class-string<OCP\EventDispatcher\IEventListener<OCP\EventDispatcher\Event>>, but OCA\Files_Sharing\Listener\MemberAddedToCircleListener::class provided (see https://psalm.dev/004)

Check failure on line 107 in apps/files_sharing/lib/AppInfo/Application.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedClass

apps/files_sharing/lib/AppInfo/Application.php:107:35: UndefinedClass: Class, interface or enum named OCA\Circles\Events\CircleMemberAddedEvent does not exist (see https://psalm.dev/019)
$context->registerEventListener(CircleMemberRemovedEvent::class, MemberRemovedFromCircleListener::class);

Check failure on line 108 in apps/files_sharing/lib/AppInfo/Application.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidArgument

apps/files_sharing/lib/AppInfo/Application.php:108:68: InvalidArgument: Argument 2 of OCP\AppFramework\Bootstrap\IRegistrationContext::registerEventListener expects class-string<OCP\EventDispatcher\IEventListener<OCP\EventDispatcher\Event>>, but OCA\Files_Sharing\Listener\MemberRemovedFromCircleListener::class provided (see https://psalm.dev/004)

Check failure on line 108 in apps/files_sharing/lib/AppInfo/Application.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedClass

apps/files_sharing/lib/AppInfo/Application.php:108:35: UndefinedClass: Class, interface or enum named OCA\Circles\Events\CircleMemberRemovedEvent does not exist (see https://psalm.dev/019)

// Publish activity for public download
$context->registerEventListener(BeforeNodeReadEvent::class, BeforeNodeReadListener::class);
Expand Down
56 changes: 56 additions & 0 deletions apps/files_sharing/lib/Listener/CircleListenerBase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Robin Appelman <[email protected]>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Files_Sharing\Listener;

use OC\User\LazyUser;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Member;
use OCP\IUser;
use OCP\IUserManager;
use Psr\Log\LoggerInterface;

class CircleListenerBase {
public function __construct(
private readonly IUserManager $userManager,
) {
}


/**
* @return \Iterator<string, IUser>
*/
protected function usersFromMember(Member $member): \Iterator {
if ($member->getUserType() === Member::TYPE_CIRCLE) {
$members = $member->getBasedOn()->getInheritedMembers();
} else {
$members = [$member];
}

foreach ($members as $member) {
if ($member->getUserType() === Member::TYPE_USER) {
yield $member->getUserId() => new LazyUser($member->getUserId(), $this->userManager);
} elseif ($member->getUserType() === Member::TYPE_GROUP) {
// todo
}
}
}

/**
* @return \Iterator<string, IUser>
*/
protected function usersFromCircle(Circle $circle): \Iterator {
foreach ($circle->getInheritedMembers() as $member) {
if ($member->getUserType() === Member::TYPE_USER) {
yield $member->getUserId() => new LazyUser($member->getUserId(), $this->userManager);
} elseif ($member->getUserType() === Member::TYPE_GROUP) {
// todo
}
}
}
}
39 changes: 39 additions & 0 deletions apps/files_sharing/lib/Listener/GroupListenerBase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Robin Appelman <[email protected]>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Files_Sharing\Listener;

use OCA\Circles\Db\CircleRequest;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\Probes\CircleProbe;

class GroupListenerBase {
private function getGroupCircle(CircleRequest $circleRequest, string $groupId): Circle {

Check failure on line 17 in apps/files_sharing/lib/Listener/GroupListenerBase.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedClass

apps/files_sharing/lib/Listener/GroupListenerBase.php:17:34: UndefinedClass: Class, interface or enum named OCA\Circles\Db\CircleRequest does not exist (see https://psalm.dev/019)
$circle = new Circle();
$circle->setName('group:' . $groupId)
->setConfig(Circle::CFG_SYSTEM | Circle::CFG_NO_OWNER | Circle::CFG_HIDDEN)
->setSource(Member::TYPE_GROUP);
return $circleRequest->searchCircle($circle);
}

/**
* @return Circle[]

Check failure on line 26 in apps/files_sharing/lib/Listener/GroupListenerBase.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedDocblockClass

apps/files_sharing/lib/Listener/GroupListenerBase.php:26:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\Circles\Model\Circle does not exist (see https://psalm.dev/200)
*/
protected function getCirclesForGroup(CircleRequest $circleRequest, string $groupId): array {

Check failure on line 28 in apps/files_sharing/lib/Listener/GroupListenerBase.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedClass

apps/files_sharing/lib/Listener/GroupListenerBase.php:28:40: UndefinedClass: Class, interface or enum named OCA\Circles\Db\CircleRequest does not exist (see https://psalm.dev/019)
$groupCircle = $this->getGroupCircle($circleRequest, $groupId);

$filterMember = new Member();
$filterMember->setSingleId($groupCircle->getSingleId());
$probe = new CircleProbe();
$probe->filterHiddenCircles()
->filterBackendCircles()
->setFilterMember($filterMember);
return $circleRequest->getCircles(null, $probe);
}
}
57 changes: 57 additions & 0 deletions apps/files_sharing/lib/Listener/MemberAddedToCircleListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files_Sharing\Listener;

use OCA\Circles\Events\CircleMemberAddedEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\EventDispatcher\IEventListener;
use OCP\IUserManager;
use OCP\Share\Events\UserAddedToShareEvent;
use OCP\Share\IManager;
use OCP\Share\IShare;

/** @template-implements IEventListener<CircleMemberAddedEvent> */
class MemberAddedToCircleListener extends CircleListenerBase implements IEventListener {

Check failure on line 21 in apps/files_sharing/lib/Listener/MemberAddedToCircleListener.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidTemplateParam

apps/files_sharing/lib/Listener/MemberAddedToCircleListener.php:21:73: InvalidTemplateParam: Extended template param T expects type OCP\EventDispatcher\Event, type OCA\Circles\Events\CircleMemberAddedEvent given (see https://psalm.dev/183)

Check failure on line 21 in apps/files_sharing/lib/Listener/MemberAddedToCircleListener.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedDocblockClass

apps/files_sharing/lib/Listener/MemberAddedToCircleListener.php:21:7: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\Circles\Events\CircleMemberAddedEvent does not exist (see https://psalm.dev/200)

public function __construct(
private readonly IManager $shareManager,
private readonly IEventDispatcher $eventDispatcher,
private readonly IUserManager $userManager,
) {
parent::__construct($this->userManager);
}

public function handle(Event $event): void {

Check failure on line 31 in apps/files_sharing/lib/Listener/MemberAddedToCircleListener.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

ImplementedParamTypeMismatch

apps/files_sharing/lib/Listener/MemberAddedToCircleListener.php:31:31: ImplementedParamTypeMismatch: Argument 1 of OCA\Files_Sharing\Listener\MemberAddedToCircleListener::handle has wrong type 'OCP\EventDispatcher\Event', expecting 'OCA\Circles\Events\CircleMemberAddedEvent' as defined by OCP\EventDispatcher\IEventListener::handle (see https://psalm.dev/199)
if (!($event instanceof CircleMemberAddedEvent)) {
return;
}

$users = $this->usersFromMember($event->getMember());
$circle = $event->getCircle();

$shares = null;
foreach ($users as $user) {
if ($shares === null) {
// we only need to get the shares for one user, the shares we're looking for are common between all users
// todo: add a way to get shares by circle id
$shares = $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_CIRCLE, null, -1);
}

foreach ($shares as $share) {
// If this is not the new circle we can skip it
if ($share->getSharedWith() !== $circle->getSingleId()) {
continue;
}

$this->eventDispatcher->dispatchTyped(new UserAddedToShareEvent($share, $user));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files_Sharing\Listener;

use OCA\Circles\Events\CircleMemberRemovedEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\EventDispatcher\IEventListener;
use OCP\IUserManager;
use OCP\Share\Events\UserRemovedFromShareEvent;
use OCP\Share\IManager;
use OCP\Share\IShare;

/** @template-implements IEventListener<CircleMemberRemovedEvent> */
class MemberRemovedFromCircleListener extends CircleListenerBase implements IEventListener {

public function __construct(
private readonly IManager $shareManager,
private readonly IEventDispatcher $eventDispatcher,
private readonly IUserManager $userManager,
) {
parent::__construct($this->userManager);
}

public function handle(Event $event): void {
if (!($event instanceof CircleMemberRemovedEvent)) {
return;
}

$circle = $event->getCircle();

$circleMembers = iterator_to_array($this->usersFromCircle($circle));
// todo: add a way to get shares by circle id
if (count($circleMembers)) {
$user = current($circleMembers);
// get the shares from a user still in the circle
$shares = $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_CIRCLE, null, -1);
} else {
// if nobody is in the circle anymore we current have to go through all shares
// todo: add a way to get shares by group id
$shares = $this->shareManager->getAllShares();
}

foreach ($shares as $share) {
// If this is not the new group we can skip it
if ($share->getShareType() === IShare::TYPE_CIRCLE && $share->getSharedWith() !== $circle->getSingleId()) {
continue;
}

foreach ($this->usersFromMember($event->getMember()) as $user) {
if (!isset($circleMembers[$user->getUID()])) {
$this->eventDispatcher->dispatchTyped(new UserRemovedFromShareEvent($share, $user));
}
}
}
}
}
57 changes: 44 additions & 13 deletions apps/files_sharing/lib/Listener/UserAddedToGroupListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,31 @@
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Files_Sharing\Listener;

use OCA\Circles\Db\CircleRequest;
use OCA\Circles\Model\Circle;
use OCA\Files_Sharing\AppInfo\Application;
use OCP\App\IAppManager;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\EventDispatcher\IEventListener;
use OCP\Group\Events\UserAddedEvent;
use OCP\IConfig;
use OCP\Share\Events\UserAddedToShareEvent;
use OCP\Share\IManager;
use OCP\Share\IShare;
use Psr\Container\ContainerInterface;

/** @template-implements IEventListener<UserAddedEvent> */
class UserAddedToGroupListener implements IEventListener {

class UserAddedToGroupListener extends GroupListenerBase implements IEventListener {
public function __construct(
private IManager $shareManager,
private IConfig $config,
private readonly IManager $shareManager,
private readonly IConfig $config,
private readonly IEventDispatcher $eventDispatcher,
private readonly IAppManager $appManager,
private readonly ContainerInterface $container,
) {
}

Expand All @@ -33,22 +42,44 @@ public function handle(Event $event): void {
$user = $event->getUser();
$group = $event->getGroup();

// This user doesn't have autoaccept so we can skip it all
if (!$this->hasAutoAccept($user->getUID())) {
return;
}
// Get all group shares this user has access too now to filter later
$groupShares = $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_GROUP, null, -1);

// Get all group shares this user has access to now to filter later
$shares = $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_GROUP, null, -1);

foreach ($shares as $share) {
foreach ($groupShares as $share) {
// If this is not the new group we can skip it
if ($share->getSharedWith() !== $group->getGID()) {
continue;
}

// Accept the share if needed
$this->shareManager->acceptShare($share, $user->getUID());
if ($this->hasAutoAccept($user->getUID())) {
$this->shareManager->acceptShare($share, $user->getUID());
}

$this->eventDispatcher->dispatchTyped(new UserAddedToShareEvent($share, $user));
}

if ($this->appManager->isAppLoaded('circles')) {
// Get all circle shares this user has access too now to filter later
$circleShares = $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_CIRCLE, null, -1);
if (count($circleShares) === 0) {
return;
}

/** @var CircleRequest $circlesRequest */
$circlesRequest = $this->container->get(CircleRequest::class);
$circles = $this->getCirclesForGroup($circlesRequest, $group->getGID());
$circleIds = array_map(fn (Circle $circle) => $circle->getSingleId(), $circles);

foreach ($circleShares as $share) {
if (!in_array($share->getSharedWith(), $circleIds)) {
continue;
}
// todo: detect if this share is new for the user because it only has access trough the new group
// or if the user already had access before

$this->eventDispatcher->dispatchTyped(new UserAddedToShareEvent($share, $user));
}
}
}

Expand Down
Loading
Loading