fix: always render detailed team permissions table in sidebar (#8108)

Remove the generic write/admin description block for AccessMode 2/3 and unconditionally display the unit-level permissions table.

fixes #3517

## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests

- I added test coverage for Go changes...
  - [ ] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I added test coverage for JavaScript changes...
  - [ ] in `web_src/js/*.test.js` if it can be unit tested.
  - [X] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [X] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [X] I want the title to show in the release notes with a link to this pull request.
- [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8108
Reviewed-by: Otto <otto@codeberg.org>
Co-authored-by: Julian Schlarb <julian.schlarb@denktmit.de>
Co-committed-by: Julian Schlarb <julian.schlarb@denktmit.de>
This commit is contained in:
Julian Schlarb 2025-06-09 10:37:31 +02:00 committed by Earl Warren
parent bd6f3243ab
commit e70f48bd44
7 changed files with 140 additions and 8 deletions

View file

@ -239,3 +239,15 @@
num_members: 2
includes_all_repositories: false
can_create_org_repo: false
-
id: 25
org_id: 17
lower_name: super-user
name: super-user
description: ""
authorize: 3
num_repos: 0
num_members: 0
includes_all_repositories: 0
can_create_org_repo: 0

View file

@ -329,3 +329,10 @@
team_id: 22
type: 3
access_mode: 1
-
id: 84
org_id: 17
team_id: 25
type: 3
access_mode: 3

View file

@ -642,7 +642,7 @@
num_following: 0
num_stars: 0
num_repos: 2
num_teams: 3
num_teams: 4
num_members: 4
visibility: 0
repo_admin_change_team_access: false

View file

@ -2985,8 +2985,6 @@ teams.invite_team_member.list = Pending invitations
teams.delete_team_title = Delete team
teams.delete_team_desc = Deleting a team revokes repository access from its members. Continue?
teams.delete_team_success = The team has been deleted.
teams.read_permission_desc = This team grants <strong>Read</strong> access: members can view and clone team repositories.
teams.write_permission_desc = This team grants <strong>Write</strong> access: members can read from and push to team repositories.
teams.admin_permission_desc = This team grants <strong>Administrator</strong> access: members can read from, push to and add collaborators to team repositories.
teams.create_repo_permission_desc = Additionally, this team grants <strong>Create repository</strong> permission: members can create new repositories in organization.
teams.repositories = Team repositories

View file

@ -42,11 +42,8 @@
<li>{{ctx.Locale.Tr "org.teams.can_create_org_repo"}}</li>
{{end}}
</ul>
{{if (eq .Team.AccessMode 2)}}
<h3>{{ctx.Locale.Tr "org.settings.permission"}}</h3>
{{ctx.Locale.Tr "org.teams.write_permission_desc"}}
{{else if (eq .Team.AccessMode 3)}}
<h3>{{ctx.Locale.Tr "org.settings.permission"}}</h3>
<h3>{{ctx.Locale.Tr "org.settings.permission"}}</h3>
{{if (eq .Team.AccessMode 3)}}
{{ctx.Locale.Tr "org.teams.admin_permission_desc"}}
{{else}}
<table class="ui table">

View file

@ -0,0 +1,116 @@
// @watch start
// templates/org/team/sidebar.tmpl
// @watch end
/* eslint playwright/expect-expect: ["error", { "assertFunctionNames": ["assertPermissionsDetails", "assertRestrictedAccess", "assertOwnerPermissions"] }] */
import {expect, type Page} from '@playwright/test';
import {test} from './utils_e2e.ts';
type Permission = 'No access' | 'Write' | 'Read';
const UNIT_VALUES = [
'Code',
'Issues',
'Pull requests',
'Releases',
'Wiki',
'External Wiki',
'External issues',
'Projects',
'Packages',
'Actions',
] as const;
type Unit = typeof UNIT_VALUES[number];
const assertPermission = async (page: Page, name: Unit, permission: Permission) => {
await expect.soft(page.getByRole('row', {name}).getByRole('cell').nth(1)).toHaveText(permission);
};
const testTeamUrl = '/org/org17/teams/test_team';
const reviewTeamUrl = '/org/org17/teams/review_team';
const ownersUrl = '/org/org17/teams/owners';
const adminUrl = '/org/org17/teams/super-user';
const cases: Record<string, { read?: Unit[], write?: Unit[] }> = {
[testTeamUrl]: {write: ['Issues']},
[reviewTeamUrl]: {read: ['Code']},
};
const assertOwnerPermissions = async (page: Page, code: number = 200) => {
const response = await page.goto(ownersUrl);
expect(response?.status()).toBe(code);
await expect(page.getByText('Owners have full access to all repositories and have administrator access to the organization.')).toBeVisible();
};
const assertAdminPermissions = async (page: Page, code: number = 200) => {
const response = await page.goto(adminUrl);
expect(response?.status()).toBe(code);
await expect(page.getByText('This team grants Administrator access: members can read from, push to and add collaborators to team repositories.')).toBeVisible();
};
const assertRestrictedAccess = async (page: Page, ...urls: string[]) => {
for (const url of urls) {
expect((await page.goto(url))?.status(), 'should not see any details').toBe(404);
}
};
const assertPermissionsDetails = async (page: Page, url: (keyof typeof cases)) => {
const response = await page.goto(url);
expect(response?.status()).toBe(200);
const per = cases[url];
for (const unit of UNIT_VALUES) {
if (per.read?.includes(unit)) {
await assertPermission(page, unit, 'Read');
} else if (per.write?.includes(unit)) {
await assertPermission(page, unit, 'Write');
} else {
await assertPermission(page, unit, 'No access');
}
}
};
test.describe('Orga team overview', () => {
test.describe('admin', () => {
test.use({user: 'user1'});
test('should see all', async ({page}) => {
await assertPermissionsDetails(page, testTeamUrl);
await assertPermissionsDetails(page, reviewTeamUrl);
await assertOwnerPermissions(page);
await assertAdminPermissions(page);
});
});
test.describe('owner', () => {
test.use({user: 'user18'});
test('should see all', async ({page}) => {
await assertPermissionsDetails(page, testTeamUrl);
await assertPermissionsDetails(page, reviewTeamUrl);
await assertOwnerPermissions(page);
await assertAdminPermissions(page);
});
});
test.describe('reviewer team', () => {
test.use({user: 'user29'});
test('should only see permissions for `reviewer team` and restricted access to other resources', async ({page}) => {
await assertPermissionsDetails(page, reviewTeamUrl);
await assertRestrictedAccess(page, ownersUrl, testTeamUrl, adminUrl);
});
});
test.describe('test_team', () => {
test.use({user: 'user2'});
test('should only see permissions for test_team and restricted access to other resources', async ({page}) => {
await assertPermissionsDetails(page, testTeamUrl);
await assertRestrictedAccess(page, ownersUrl, reviewTeamUrl, adminUrl);
});
});
});

View file

@ -94,6 +94,8 @@ func createSessions(t testing.TB) {
"user1",
"user2",
"user12",
"user18",
"user29",
"user40",
}