Skip to content

Feature/implement default quota for groups - #2858

Open
hefftich wants to merge 14 commits into
nextcloud:masterfrom
Dataport:feature/implement_default_Quota_for_Groups
Open

Feature/implement default quota for groups#2858
hefftich wants to merge 14 commits into
nextcloud:masterfrom
Dataport:feature/implement_default_Quota_for_Groups

Conversation

@hefftich

Copy link
Copy Markdown

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

  • Bob is a member in the groups marketing and engineering. He creates a new team, which gets a quota of 5 GB.
  • Alice is not a member of any groups defined in this list. Her newly created team gets a quota of 100 MB.

TODO

  • ...

Checklist

AI (if applicable)

  • The content of this PR was partly or fully generated using AI
autocreation disabled banner

Fin-c and others added 7 commits August 28, 2026 10:43
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>
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

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Co-authored-by: Copilot <copilot@github.com>

Signed-off-by: Stefan Lender <Stefan.Lender@dataport.de>
@tomek2k1 tomek2k1 moved this from Backlog to In progress in 👥 Teams Sep 2, 2026
@tomek2k1 tomek2k1 moved this from In progress to In review in 👥 Teams Sep 2, 2026
Comment thread appinfo/info.xml
Comment thread lib/ConfigLexicon.php Outdated
hefftich and others added 3 commits September 2, 2026 14:32
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>
@hefftich
hefftich enabled auto-merge September 2, 2026 14:08
Co-authored-by: Copilot <copilot@github.com>

Signed-off-by: Stefan Lender <Stefan.Lender@dataport.de>
Comment thread appinfo/info.xml
Comment thread lib/ConfigLexicon.php
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),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we have team settings or properties or metadata to store such data? cc @cristianscheid who might know

@cristianscheid cristianscheid Sep 2, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread src/teams/api.ts
Comment on lines +302 to +320
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
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This already existe, no?

Comment on lines +130 to +137
public function removeTeam(string $teamId): void {
$quotas = $this->getQuotas();
if (array_key_exists($teamId, $quotas)) {
unset($quotas[$teamId]);
$this->setQuotas($quotas);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would not be necessary if we store the quota info with the team properties.

Comment thread lib/ConfigLexicon.php
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),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you reuse DEFAULT_QUOTA here?

Comment on lines +83 to +102
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(),
]);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to this PR, right?

* @return DataResponse
* @throws OCSException
*/
public function teamFolders(): DataResponse {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to this PR, no?

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;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to add a comment here to explain what this number represents, to avoid guessing. Maybe something like:

Suggested change
public const DEFAULT_QUOTA = 104857600;
public const DEFAULT_QUOTA = 104857600; // 104857600 bytes ~ 100 MB

@hefftich hefftich self-assigned this Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

5 participants