Skip to content

Commit 61e012e

Browse files
authored
Merge pull request #4989 from nextcloud/feature/teams-auto-folder-creation
feat: add event handling to auto create teamfolders upon circle creation
2 parents 3f05bcd + ffea11b commit 61e012e

25 files changed

Lines changed: 1326 additions & 398 deletions

‎lib/AppInfo/Application.php‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
use OCA\GroupFolders\Listeners\NodeRenamedListener;
3333
use OCA\GroupFolders\Mount\FolderStorageManager;
3434
use OCA\GroupFolders\Mount\MountProvider;
35+
use OCA\GroupFolders\TeamSpace\TeamSpaceProvider;
3536
use OCA\GroupFolders\Trash\TrashBackend;
3637
use OCA\GroupFolders\Trash\TrashManager;
3738
use OCA\GroupFolders\Versions\GroupVersionsExpireManager;
@@ -86,6 +87,7 @@ public function register(IRegistrationContext $context): void {
8687
$context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalScriptsListener::class);
8788
$context->registerEventListener(BeforeTemplateRenderedEvent::class, LoadAdditionalScriptsListener::class);
8889
$context->registerEventListener(CircleDestroyedEvent::class, CircleDestroyedEventListener::class);
90+
$context->registerTeamResourceProvider(TeamSpaceProvider::class);
8991
$context->registerEventListener(NodeRenamedEvent::class, NodeRenamedListener::class);
9092
$context->registerEventListener(CacheEntryInsertedEvent::class, CacheListener::class, 99999);
9193
$context->registerEventListener(CacheEntryUpdatedEvent::class, CacheListener::class, 99999);

‎lib/Command/Delete.php‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
3131
return -1;
3232
}
3333

34+
if ($folder->isTeamSpace()) {
35+
$output->writeln('<error>This folder belongs to a team and cannot be deleted directly; unlink it from its team first.</error>');
36+
return 1;
37+
}
38+
3439
/** @var QuestionHelper $helper */
3540
$helper = $this->getHelper('question');
3641
$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);

‎lib/Command/Group.php‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
7171
}
7272

7373
$this->folderManager->setGroupPermissions($folder->id, $groupString, $permissions);
74-
7574
return 0;
7675
}
7776

‎lib/Command/Rename.php‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
2828
return -1;
2929
}
3030

31+
if ($folder->isTeamSpace()) {
32+
$output->writeln('<error>This folder belongs to a team and cannot be renamed; unlink it from its team first.</error>');
33+
return 1;
34+
}
35+
3136
/** @var string $name */
3237
$name = $input->getArgument('name');
3338

‎lib/Controller/FolderController.php‎

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
* acl_default_no_permission: bool,
5454
* manage: list<GroupFoldersAclManage>,
5555
* sortIndex?: non-negative-int,
56+
* team_circle_id?: ?string,
5657
* }
5758
*/
5859
class FolderController extends OCSController {
@@ -110,6 +111,7 @@ private function formatFolder(FolderWithMappingsAndCache $folder): array {
110111
'groups' => array_map(fn (array $group): int => $group['permissions'], $folder->groups),
111112
'group_details' => $folder->groups,
112113
'manage' => $folder->manage,
114+
'team_circle_id' => $folder->teamCircleId,
113115
];
114116
}
115117

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

298+
if ($folder->isTeamSpace()) {
299+
throw new OCSForbiddenException('This folder belongs to a team and cannot be deleted directly; unlink it from its team first');
300+
}
301+
296302
$this->folderStorageManager->deleteStoragesForFolder($folder);
297303
$this->manager->removeFolder($id);
298304

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

353+
if ($folder->isTeamSpace()) {
354+
throw new OCSForbiddenException('This folder belongs to a team and cannot be shared with other groups; unlink it from its team first');
355+
}
356+
347357
if (array_key_exists($group, $folder->groups)) {
348358
throw new OCSBadRequestException('Group already assigned to this Groupfolder');
349359
}
@@ -370,7 +380,13 @@ public function addGroup(int $id, string $group): DataResponse {
370380
#[NoAdminRequired]
371381
#[FrontpageRoute(verb: 'DELETE', url: '/folders/{id}/groups/{group}', requirements: ['group' => '.+'])]
372382
public function removeGroup(int $id, string $group): DataResponse {
373-
$this->checkedGetFolder($id);
383+
$folder = $this->checkedGetFolder($id);
384+
385+
// The owning team's access cannot be removed independently; the folder
386+
// must be unlinked from its team first.
387+
if ($folder->isTeamSpace() && $folder->teamCircleId === $group) {
388+
throw new OCSForbiddenException('This folder belongs to this team and its access cannot be removed independently; unlink the folder from the team first');
389+
}
374390

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

@@ -395,7 +411,12 @@ public function removeGroup(int $id, string $group): DataResponse {
395411
#[NoAdminRequired]
396412
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/groups/{group}', requirements: ['group' => '.+'])]
397413
public function setPermissions(int $id, string $group, int $permissions): DataResponse {
398-
$this->checkedGetFolder($id);
414+
$folder = $this->checkedGetFolder($id);
415+
416+
// The owning team's permissions on a team space are fixed.
417+
if ($folder->isTeamSpace() && $folder->teamCircleId === $group) {
418+
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');
419+
}
399420

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

@@ -421,7 +442,11 @@ public function setPermissions(int $id, string $group, int $permissions): DataRe
421442
#[NoAdminRequired]
422443
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/manageACL')]
423444
public function setManageACL(int $id, string $mappingType, string $mappingId, bool $manageAcl): DataResponse {
424-
$this->checkedGetFolder($id);
445+
$folder = $this->checkedGetFolder($id);
446+
447+
if ($folder->isTeamSpace()) {
448+
throw new OCSForbiddenException('This folder belongs to a team and its ACL management cannot be changed independently; unlink it from its team first');
449+
}
425450

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

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

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

@@ -469,7 +494,11 @@ public function setQuota(int $id, int $quota): DataResponse {
469494
#[NoAdminRequired]
470495
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/acl')]
471496
public function setACL(int $id, bool $acl): DataResponse {
472-
$this->checkedGetFolder($id);
497+
$folder = $this->checkedGetFolder($id);
498+
499+
if ($folder->isTeamSpace()) {
500+
throw new OCSForbiddenException('This folder belongs to a team and its advanced permissions cannot be changed independently; unlink it from its team first');
501+
}
473502

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

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

499-
$this->checkedGetFolder($id);
500-
501528
$folder = $this->checkedGetFolder($id);
502529

530+
if ($folder->isTeamSpace()) {
531+
throw new OCSForbiddenException('This folder belongs to a team and cannot be renamed; unlink it from its team first');
532+
}
533+
503534
if ($folder->mountPoint === $mountpoint) {
504535
return new DataResponse(['success' => true, 'folder' => $this->formatFolder($folder)]);
505536
}

‎lib/Folder/FolderDefinition.php‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
class FolderDefinition {
1212
/**
1313
* @param array{separate-storage?: bool} $options
14+
* @param ?string $teamCircleId The circle single id this team space belongs to; null for regular team folders.
1415
*/
1516
public function __construct(
1617
public readonly int $id,
@@ -21,10 +22,19 @@ public function __construct(
2122
public readonly int $storageId,
2223
public readonly int $rootId,
2324
public readonly array $options,
25+
public readonly ?string $teamCircleId = null,
2426
) {
2527
}
2628

2729
public function useSeparateStorage(): bool {
2830
return $this->options['separate-storage'] ?? false;
2931
}
32+
33+
/**
34+
* Whether this folder belongs to a team (i.e. is a team space) and
35+
* therefore cannot be deleted or renamed independently of that team.
36+
*/
37+
public function isTeamSpace(): bool {
38+
return $this->teamCircleId !== null;
39+
}
3040
}

‎lib/Folder/FolderDefinitionWithMappings.php‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class FolderDefinitionWithMappings extends FolderDefinition {
1818
/**
1919
* @param array<string, GroupFoldersApplicable> $groups
2020
* @param list<GroupFoldersAclManage> $manage
21+
* @param ?string $teamCircleId
2122
*/
2223
public function __construct(
2324
int $id,
@@ -30,8 +31,9 @@ public function __construct(
3031
array $options,
3132
public readonly array $groups,
3233
public readonly array $manage,
34+
?string $teamCircleId = null,
3335
) {
34-
parent::__construct($id, $mountPoint, $quota, $acl, $aclDefaultNoPermission, $storageId, $rootId, $options);
36+
parent::__construct($id, $mountPoint, $quota, $acl, $aclDefaultNoPermission, $storageId, $rootId, $options, $teamCircleId);
3537
}
3638

3739
/**
@@ -50,6 +52,7 @@ public static function fromFolder(FolderDefinition $folder, array $groups, array
5052
$folder->options,
5153
$groups,
5254
$manage,
55+
$folder->teamCircleId,
5356
);
5457
}
5558

‎lib/Folder/FolderDefinitionWithPermissions.php‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ public function __construct(
2727
array $options,
2828
public readonly ICacheEntry $rootCacheEntry,
2929
public readonly int $permissions,
30+
?string $teamCircleId = null,
3031
) {
31-
parent::__construct($id, $mountPoint, $quota, $acl, $aclDefaultNoPermission, $storageId, $rootId, $options);
32+
parent::__construct($id, $mountPoint, $quota, $acl, $aclDefaultNoPermission, $storageId, $rootId, $options, $teamCircleId);
3233
}
3334

3435
public static function fromFolder(FolderDefinition $folder, ICacheEntry $rootCacheEntry, int $permissions): FolderDefinitionWithPermissions {
@@ -43,6 +44,7 @@ public static function fromFolder(FolderDefinition $folder, ICacheEntry $rootCac
4344
$folder->options,
4445
$rootCacheEntry,
4546
$permissions,
47+
$folder->teamCircleId,
4648
);
4749
}
4850

@@ -85,6 +87,7 @@ public function withAddedPermissions(int $permissions): self {
8587
$this->options,
8688
$this->rootCacheEntry,
8789
$this->permissions | $permissions,
90+
$this->teamCircleId,
8891
);
8992
}
9093
}

0 commit comments

Comments
 (0)