Skip to content
Open
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
20 changes: 10 additions & 10 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

namespace OCA\GroupFolders\AppInfo;

use OCA\Circles\Events\CircleDestroyedEvent;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent;
Expand All @@ -26,12 +25,12 @@
use OCA\GroupFolders\Command\ExpireGroup\ExpireGroupVersionsTrash;
use OCA\GroupFolders\Folder\FolderManager;
use OCA\GroupFolders\Listeners\CacheListener;
use OCA\GroupFolders\Listeners\CircleDestroyedEventListener;
use OCA\GroupFolders\Listeners\DeleteListener;
use OCA\GroupFolders\Listeners\LoadAdditionalScriptsListener;
use OCA\GroupFolders\Listeners\NodeRenamedListener;
use OCA\GroupFolders\Mount\FolderStorageManager;
use OCA\GroupFolders\Mount\MountProvider;
use OCA\GroupFolders\TeamSpace\TeamSpaceProvider;
use OCA\GroupFolders\Trash\TrashBackend;
use OCA\GroupFolders\Trash\TrashManager;
use OCA\GroupFolders\Versions\GroupVersionsExpireManager;
Expand Down Expand Up @@ -85,7 +84,7 @@ public function register(IRegistrationContext $context): void {

$context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalScriptsListener::class);
$context->registerEventListener(BeforeTemplateRenderedEvent::class, LoadAdditionalScriptsListener::class);
$context->registerEventListener(CircleDestroyedEvent::class, CircleDestroyedEventListener::class);
$context->registerTeamResourceProvider(TeamSpaceProvider::class);
$context->registerEventListener(NodeRenamedEvent::class, NodeRenamedListener::class);
$context->registerEventListener(CacheEntryInsertedEvent::class, CacheListener::class, 99999);
$context->registerEventListener(CacheEntryUpdatedEvent::class, CacheListener::class, 99999);
Expand Down
5 changes: 5 additions & 0 deletions lib/Command/Delete.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return -1;
}

if ($folder->isTeamSpace()) {
$output->writeln('<error>This folder belongs to a team and cannot be deleted directly; unlink it from its team first.</error>');
return 1;
}

/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$question = new ConfirmationQuestion('Are you sure you want to delete the Team folder ' . $folder->mountPoint . ' and all files within, this cannot be undone (y/N).', false);
Expand Down
1 change: 0 additions & 1 deletion lib/Command/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

$this->folderManager->setGroupPermissions($folder->id, $groupString, $permissions);

return 0;
}

Expand Down
5 changes: 5 additions & 0 deletions lib/Command/Rename.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return -1;
}

if ($folder->isTeamSpace()) {
$output->writeln('<error>This folder belongs to a team and cannot be renamed; unlink it from its team first.</error>');
return 1;
}

/** @var string $name */
$name = $input->getArgument('name');

Expand Down
45 changes: 38 additions & 7 deletions lib/Controller/FolderController.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
* acl_default_no_permission: bool,
* manage: list<GroupFoldersAclManage>,
* sortIndex?: non-negative-int,
* team_circle_id?: ?string,
* }
*/
class FolderController extends OCSController {
Expand Down Expand Up @@ -110,6 +111,7 @@ private function formatFolder(FolderWithMappingsAndCache $folder): array {
'groups' => array_map(fn (array $group): int => $group['permissions'], $folder->groups),
'group_details' => $folder->groups,
'manage' => $folder->manage,
'team_circle_id' => $folder->getTeamCircleId(),
];
}

Expand Down Expand Up @@ -293,6 +295,10 @@ public function addFolder(string $mountpoint, ?string $bucket = null, bool $acl_
public function removeFolder(int $id): DataResponse {
$folder = $this->checkedGetFolder($id);

if ($folder->isTeamSpace()) {
throw new OCSForbiddenException('This folder belongs to a team and cannot be deleted directly; unlink it from its team first');
}

$this->folderStorageManager->deleteStoragesForFolder($folder);
$this->manager->removeFolder($id);

Expand Down Expand Up @@ -344,6 +350,10 @@ public function setMountPoint(int $id, string $mountPoint): DataResponse {
public function addGroup(int $id, string $group): DataResponse {
$folder = $this->checkedGetFolder($id);

if ($folder->isTeamSpace()) {
throw new OCSForbiddenException('This folder belongs to a team and cannot be shared with other groups; unlink it from its team first');
}

if (array_key_exists($group, $folder->groups)) {
throw new OCSBadRequestException('Group already assigned to this Groupfolder');
}
Expand All @@ -370,7 +380,13 @@ public function addGroup(int $id, string $group): DataResponse {
#[NoAdminRequired]
#[FrontpageRoute(verb: 'DELETE', url: '/folders/{id}/groups/{group}', requirements: ['group' => '.+'])]
public function removeGroup(int $id, string $group): DataResponse {
$this->checkedGetFolder($id);
$folder = $this->checkedGetFolder($id);

// The owning team's access cannot be removed independently; the folder
// must be unlinked from its team first.
if ($folder->isTeamSpace() && $folder->getTeamCircleId() === $group) {
throw new OCSForbiddenException('This folder belongs to this team and its access cannot be removed independently; unlink the folder from the team first');
}

$this->manager->removeApplicableGroup($id, $group);

Expand All @@ -395,7 +411,12 @@ public function removeGroup(int $id, string $group): DataResponse {
#[NoAdminRequired]
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/groups/{group}', requirements: ['group' => '.+'])]
public function setPermissions(int $id, string $group, int $permissions): DataResponse {
$this->checkedGetFolder($id);
$folder = $this->checkedGetFolder($id);

// The owning team's permissions on a team space are fixed.
if ($folder->isTeamSpace() && $folder->getTeamCircleId() === $group) {
throw new OCSForbiddenException('This team space belongs to this team and its permissions cannot be changed independently; unlink the team space from the team first');
}

$this->manager->setGroupPermissions($id, $group, $permissions);

Expand All @@ -421,7 +442,11 @@ public function setPermissions(int $id, string $group, int $permissions): DataRe
#[NoAdminRequired]
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/manageACL')]
public function setManageACL(int $id, string $mappingType, string $mappingId, bool $manageAcl): DataResponse {
$this->checkedGetFolder($id);
$folder = $this->checkedGetFolder($id);

if ($folder->isTeamSpace()) {
throw new OCSForbiddenException('This folder belongs to a team and its ACL management cannot be changed independently; unlink it from its team first');
}

$this->manager->setManageACL($id, $mappingType, $mappingId, $manageAcl);

Expand All @@ -445,7 +470,7 @@ public function setManageACL(int $id, string $mappingType, string $mappingId, bo
#[NoAdminRequired]
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/quota')]
public function setQuota(int $id, int $quota): DataResponse {
$this->checkedGetFolder($id);
$folder = $this->checkedGetFolder($id);

$this->manager->setFolderQuota($id, $quota);

Expand All @@ -469,7 +494,11 @@ public function setQuota(int $id, int $quota): DataResponse {
#[NoAdminRequired]
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/acl')]
public function setACL(int $id, bool $acl): DataResponse {
$this->checkedGetFolder($id);
$folder = $this->checkedGetFolder($id);

if ($folder->isTeamSpace()) {
throw new OCSForbiddenException('This folder belongs to a team and its advanced permissions cannot be changed independently; unlink it from its team first');
}

$this->manager->setFolderACL($id, $acl);

Expand All @@ -496,10 +525,12 @@ public function setACL(int $id, bool $acl): DataResponse {
public function renameFolder(int $id, string $mountpoint): DataResponse {
$mountpoint = $this->manager->trimMountpoint($mountpoint);

$this->checkedGetFolder($id);

$folder = $this->checkedGetFolder($id);

if ($folder->isTeamSpace()) {
throw new OCSForbiddenException('This folder belongs to a team and cannot be renamed; unlink it from its team first');
}

if ($folder->mountPoint === $mountpoint) {
return new DataResponse(['success' => true, 'folder' => $this->formatFolder($folder)]);
}
Expand Down
18 changes: 18 additions & 0 deletions lib/Folder/FolderDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
class FolderDefinition {
/**
* @param array{separate-storage?: bool} $options
* @param ?string $teamCircleId The circle single id this team space belongs to; null for regular team folders.
*/
public function __construct(
public readonly int $id,
Expand All @@ -21,10 +22,27 @@ public function __construct(
public readonly int $storageId,
public readonly int $rootId,
public readonly array $options,
public readonly ?string $teamCircleId = null,
) {
}

public function useSeparateStorage(): bool {
return $this->options['separate-storage'] ?? false;
}

/**
* Whether this folder belongs to a team (i.e. is a team space) and
* therefore cannot be deleted or renamed independently of that team.
*/
public function isTeamSpace(): bool {
return $this->teamCircleId !== null;
}

/**
* The circle single id this team space belongs to, or null for regular
* team folders.
*/
public function getTeamCircleId(): ?string {
return $this->teamCircleId;
}
}
5 changes: 4 additions & 1 deletion lib/Folder/FolderDefinitionWithMappings.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class FolderDefinitionWithMappings extends FolderDefinition {
/**
* @param array<string, GroupFoldersApplicable> $groups
* @param list<GroupFoldersAclManage> $manage
* @param ?string $teamCircleId
*/
public function __construct(
int $id,
Expand All @@ -30,8 +31,9 @@ public function __construct(
array $options,
public readonly array $groups,
public readonly array $manage,
?string $teamCircleId = null,
) {
parent::__construct($id, $mountPoint, $quota, $acl, $aclDefaultNoPermission, $storageId, $rootId, $options);
parent::__construct($id, $mountPoint, $quota, $acl, $aclDefaultNoPermission, $storageId, $rootId, $options, $teamCircleId);
}

/**
Expand All @@ -50,6 +52,7 @@ public static function fromFolder(FolderDefinition $folder, array $groups, array
$folder->options,
$groups,
$manage,
$folder->teamCircleId,
);
}

Expand Down
5 changes: 4 additions & 1 deletion lib/Folder/FolderDefinitionWithPermissions.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ public function __construct(
array $options,
public readonly ICacheEntry $rootCacheEntry,
public readonly int $permissions,
?string $teamCircleId = null,
) {
parent::__construct($id, $mountPoint, $quota, $acl, $aclDefaultNoPermission, $storageId, $rootId, $options);
parent::__construct($id, $mountPoint, $quota, $acl, $aclDefaultNoPermission, $storageId, $rootId, $options, $teamCircleId);
}

public static function fromFolder(FolderDefinition $folder, ICacheEntry $rootCacheEntry, int $permissions): FolderDefinitionWithPermissions {
Expand All @@ -43,6 +44,7 @@ public static function fromFolder(FolderDefinition $folder, ICacheEntry $rootCac
$folder->options,
$rootCacheEntry,
$permissions,
$folder->teamCircleId,
);
}

Expand Down Expand Up @@ -85,6 +87,7 @@ public function withAddedPermissions(int $permissions): self {
$this->options,
$this->rootCacheEntry,
$this->permissions | $permissions,
$this->teamCircleId,
);
}
}
Loading