Feature/implement default quota for groups - #2858
Conversation
Co-authored-by: Copilot <copilot@github.com> GPT-5.6 Terra Signed-off-by: Fin Clausen <Fin.Clausen@dataport.de>
Co-authored-by: Copilot <copilot@github.com> Signed-off-by: Fin Clausen <Fin.Clausen@dataport.de>
…/implement_default_Quota_for_Groups # Conflicts: # src/components/AdminTeamFolders.vue
Co-authored-by: Copilot <copilot@github.com> Signed-off-by: Fin Clausen <Fin.Clausen@dataport.de>
Co-authored-by: Copilot <copilot@github.com> Signed-off-by: Fin Clausen <Fin.Clausen@dataport.de>
…bled Co-authored-by: Copilot <copilot@github.com> Signed-off-by: Stefan Lender <Stefan.Lender@dataport.de>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…efault_Quota_for_Groups
Co-authored-by: Copilot <copilot@github.com> Signed-off-by: Stefan Lender <Stefan.Lender@dataport.de>
Co-authored-by: Copilot <copilot@github.com> Signed-off-by: Stefan Lender <Stefan.Lender@dataport.de>
Co-authored-by: Copilot <copilot@github.com> Signed-off-by: Stefan Lender <Stefan.Lender@dataport.de>
Co-authored-by: Copilot <copilot@github.com> Signed-off-by: Stefan Lender <Stefan.Lender@dataport.de>
Co-authored-by: Copilot <copilot@github.com> Signed-off-by: Stefan Lender <Stefan.Lender@dataport.de>
| new Entry(key: self::TEAM_FOLDER_AUTO_CREATE, type: ValueType::BOOL, defaultRaw: true, definition: 'whether Circles auto-creates team folders and allows UI/API upgrade (not exposed in admin UI)', lazy: true), | ||
| new Entry(key: self::TEAM_FOLDER_DEFAULT_QUOTA, type: ValueType::INT, defaultRaw: 0, definition: 'default quota in bytes for auto-created team folders (0 means unlimited)', lazy: true), | ||
| new Entry(key: self::TEAM_FOLDER_DEFAULT_QUOTA, type: ValueType::INT, defaultRaw: 104857600, definition: 'default quota in bytes for all team folders (0 means unlimited)', lazy: true), | ||
| new Entry(key: self::TEAM_FOLDER_QUOTAS, type: ValueType::ARRAY, defaultRaw: [], definition: 'team folder quota overrides in bytes by team ID (0 means unlimited)', lazy: true), |
There was a problem hiding this comment.
Don't we have team settings or properties or metadata to store such data? cc @cristianscheid who might know
There was a problem hiding this comment.
team settings can be set/updated like here
// lib/Service/MembershipService::calculateAndSavePopulation()
$settings = $circle->getSettings();
$settings['population'] = $population;
$settings['populationInherited'] = $populationInherited;
$this->circleRequest->updateSettings($circle->setSettings($settings));those get stored in oc_circles_circle.settings
| export async function getLinkableTeamFolders(teamId: string): Promise<TeamFolder[]> { | ||
| const { data } = await axios.get<OcsResponse<TeamFolder[]>>(generateOcsUrl('apps/circles/teams/{circleId}/folder/linkable', { circleId: teamId })) | ||
| return data.ocs.data | ||
| } | ||
|
|
||
| /** | ||
| * Link a team to a team folder. | ||
| * | ||
| * @param teamId - The team single id | ||
| * @param folderId - The id of the existing team folder | ||
| */ | ||
| export async function linkTeamFolder(teamId: string, folderId: number): Promise<TeamFolder> { | ||
| const { data } = await axios.post<OcsResponse<{ folderId: number, folder: TeamFolder }>>( | ||
| generateOcsUrl('apps/circles/teams/{circleId}/folder/link', { circleId: teamId }), | ||
| { folderId }, | ||
| ) | ||
| return data.ocs.data.folder | ||
| } | ||
|
|
| public function removeTeam(string $teamId): void { | ||
| $quotas = $this->getQuotas(); | ||
| if (array_key_exists($teamId, $quotas)) { | ||
| unset($quotas[$teamId]); | ||
| $this->setQuotas($quotas); | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Would not be necessary if we store the quota info with the team properties.
| new Entry(key: self::REMOVE_SHARE_TOKENS_DONE, type: ValueType::BOOL, defaultRaw: false, definition: 'whether the remove share tokens repair step has already been executed', lazy: true), | ||
| new Entry(key: self::TEAM_FOLDER_AUTO_CREATE, type: ValueType::BOOL, defaultRaw: true, definition: 'whether Circles auto-creates team folders and allows UI/API upgrade (not exposed in admin UI)', lazy: true), | ||
| new Entry(key: self::TEAM_FOLDER_DEFAULT_QUOTA, type: ValueType::INT, defaultRaw: 0, definition: 'default quota in bytes for auto-created team folders (0 means unlimited)', lazy: true), | ||
| new Entry(key: self::TEAM_FOLDER_DEFAULT_QUOTA, type: ValueType::INT, defaultRaw: 104857600, definition: 'default quota in bytes for all team folders (0 means unlimited)', lazy: true), |
There was a problem hiding this comment.
Can you reuse DEFAULT_QUOTA here?
| public function getLinkableTeamFolders(string $circleId): DataResponse { | ||
| $this->assertAuthenticatedUserIsTeamOwnerOrServerAdmin($circleId); | ||
|
|
||
| return new DataResponse(array_map( | ||
| static fn (\OCP\Teams\TeamFolder $folder): array => $folder->jsonSerialize(), | ||
| $this->getProvider()->getLinkableTeamFolders($circleId), | ||
| )); | ||
| } | ||
|
|
||
| #[NoAdminRequired] | ||
| public function linkTeamFolder(string $circleId, int $folderId): DataResponse { | ||
| $this->assertAuthenticatedUserIsTeamOwnerOrServerAdmin($circleId); | ||
| $folder = $this->getProvider()->linkTeamFolder($circleId, $folderId); | ||
|
|
||
| return new DataResponse([ | ||
| 'success' => true, | ||
| 'folderId' => $folder->getId(), | ||
| 'folder' => $folder->jsonSerialize(), | ||
| ]); | ||
| } |
There was a problem hiding this comment.
Unrelated to this PR, right?
| * @return DataResponse | ||
| * @throws OCSException | ||
| */ | ||
| public function teamFolders(): DataResponse { |
Co-authored-by: Copilot copilot@github.com Signed-off-by: Stefan Lender <Stefan.Lender@dataport.de>
| * never persists a Groupfolders identifier. | ||
| */ | ||
| class TeamFolderPolicy { | ||
| public const DEFAULT_QUOTA = 104857600; |
There was a problem hiding this comment.
Would be nice to add a comment here to explain what this number represents, to avoid guessing. Maybe something like:
| public const DEFAULT_QUOTA = 104857600; | |
| public const DEFAULT_QUOTA = 104857600; // 104857600 bytes ~ 100 MB |
Implement a default quota per group. groupfolders#5029
Summary
We need a way for admins to set default quotas for team folder creation, based on group membership. This needs a group -> quota mapping in the Teams admin settings.
Admin settings --> Teams
- In the Team settings, admins can add entries to a table, defining default quotas for groups.
- When a user creates a new team, the highest quota of the groups, this user is a member of, gets applied to the new Team folder.
- At least a line for "all" is always present in the table, which is applied at team creation, when the user isn't a member of any other quota group.
- The three-dot menu lets admins delete any mapping, despite "all".
- The quotas only get applied at team creation. An admin can alway change the quota of existing teams later on.
group | default quota | options -- | -- | -- all | 100 MB | marketing | 2 GB | ... sales | 10 GB | ... engineering | 5 GB | ...Examples
marketingandengineering. He creates a new team, which gets a quota of 5 GB.TODO
Checklist
3. to review, feature component)stable32)AI (if applicable)