i18n: UX improvements: Team permissions and issue closing
Change word order for issue comment actions - An attempt to address https://codeberg.org/forgejo/forgejo/issues/2650 Org team permissions improvements - consistency: added missing dot - clarity: explain what external units mean - use dedicated keys to explain the permissions. - split in read/write permissions - use explicit labels for accessibility - ext_wiki.desc and ext_issues.desc are no longer in use.
This commit is contained in:
parent
60bcdc8bc3
commit
dc9a268d3c
4 changed files with 59 additions and 15 deletions
|
@ -245,6 +245,7 @@ func (u *Type) CanBeDefault() bool {
|
||||||
// Unit is a section of one repository
|
// Unit is a section of one repository
|
||||||
type Unit struct {
|
type Unit struct {
|
||||||
Type Type
|
Type Type
|
||||||
|
Name string
|
||||||
NameKey string
|
NameKey string
|
||||||
URI string
|
URI string
|
||||||
DescKey string
|
DescKey string
|
||||||
|
@ -272,6 +273,7 @@ func (u Unit) MaxPerm() perm.AccessMode {
|
||||||
var (
|
var (
|
||||||
UnitCode = Unit{
|
UnitCode = Unit{
|
||||||
TypeCode,
|
TypeCode,
|
||||||
|
"code",
|
||||||
"repo.code",
|
"repo.code",
|
||||||
"/",
|
"/",
|
||||||
"repo.code.desc",
|
"repo.code.desc",
|
||||||
|
@ -281,6 +283,7 @@ var (
|
||||||
|
|
||||||
UnitIssues = Unit{
|
UnitIssues = Unit{
|
||||||
TypeIssues,
|
TypeIssues,
|
||||||
|
"issues",
|
||||||
"repo.issues",
|
"repo.issues",
|
||||||
"/issues",
|
"/issues",
|
||||||
"repo.issues.desc",
|
"repo.issues.desc",
|
||||||
|
@ -290,6 +293,7 @@ var (
|
||||||
|
|
||||||
UnitExternalTracker = Unit{
|
UnitExternalTracker = Unit{
|
||||||
TypeExternalTracker,
|
TypeExternalTracker,
|
||||||
|
"ext_issues",
|
||||||
"repo.ext_issues",
|
"repo.ext_issues",
|
||||||
"/issues",
|
"/issues",
|
||||||
"repo.ext_issues.desc",
|
"repo.ext_issues.desc",
|
||||||
|
@ -299,6 +303,7 @@ var (
|
||||||
|
|
||||||
UnitPullRequests = Unit{
|
UnitPullRequests = Unit{
|
||||||
TypePullRequests,
|
TypePullRequests,
|
||||||
|
"pulls",
|
||||||
"repo.pulls",
|
"repo.pulls",
|
||||||
"/pulls",
|
"/pulls",
|
||||||
"repo.pulls.desc",
|
"repo.pulls.desc",
|
||||||
|
@ -308,6 +313,7 @@ var (
|
||||||
|
|
||||||
UnitReleases = Unit{
|
UnitReleases = Unit{
|
||||||
TypeReleases,
|
TypeReleases,
|
||||||
|
"releases",
|
||||||
"repo.releases",
|
"repo.releases",
|
||||||
"/releases",
|
"/releases",
|
||||||
"repo.releases.desc",
|
"repo.releases.desc",
|
||||||
|
@ -317,6 +323,7 @@ var (
|
||||||
|
|
||||||
UnitWiki = Unit{
|
UnitWiki = Unit{
|
||||||
TypeWiki,
|
TypeWiki,
|
||||||
|
"wiki",
|
||||||
"repo.wiki",
|
"repo.wiki",
|
||||||
"/wiki",
|
"/wiki",
|
||||||
"repo.wiki.desc",
|
"repo.wiki.desc",
|
||||||
|
@ -326,6 +333,7 @@ var (
|
||||||
|
|
||||||
UnitExternalWiki = Unit{
|
UnitExternalWiki = Unit{
|
||||||
TypeExternalWiki,
|
TypeExternalWiki,
|
||||||
|
"ext_wiki",
|
||||||
"repo.ext_wiki",
|
"repo.ext_wiki",
|
||||||
"/wiki",
|
"/wiki",
|
||||||
"repo.ext_wiki.desc",
|
"repo.ext_wiki.desc",
|
||||||
|
@ -335,6 +343,7 @@ var (
|
||||||
|
|
||||||
UnitProjects = Unit{
|
UnitProjects = Unit{
|
||||||
TypeProjects,
|
TypeProjects,
|
||||||
|
"projects",
|
||||||
"repo.projects",
|
"repo.projects",
|
||||||
"/projects",
|
"/projects",
|
||||||
"repo.projects.desc",
|
"repo.projects.desc",
|
||||||
|
@ -344,6 +353,7 @@ var (
|
||||||
|
|
||||||
UnitPackages = Unit{
|
UnitPackages = Unit{
|
||||||
TypePackages,
|
TypePackages,
|
||||||
|
"packages",
|
||||||
"repo.packages",
|
"repo.packages",
|
||||||
"/packages",
|
"/packages",
|
||||||
"packages.desc",
|
"packages.desc",
|
||||||
|
@ -353,6 +363,7 @@ var (
|
||||||
|
|
||||||
UnitActions = Unit{
|
UnitActions = Unit{
|
||||||
TypeActions,
|
TypeActions,
|
||||||
|
"actions",
|
||||||
"repo.actions",
|
"repo.actions",
|
||||||
"/actions",
|
"/actions",
|
||||||
"actions.unit.desc",
|
"actions.unit.desc",
|
||||||
|
|
|
@ -1441,8 +1441,7 @@ commitstatus.failure = Failure
|
||||||
commitstatus.pending = Pending
|
commitstatus.pending = Pending
|
||||||
commitstatus.success = Success
|
commitstatus.success = Success
|
||||||
|
|
||||||
ext_issues = Access to external issues
|
ext_issues = External issues
|
||||||
ext_issues.desc = Link to an external issue tracker.
|
|
||||||
|
|
||||||
projects = Projects
|
projects = Projects
|
||||||
projects.desc = Manage issues and pulls in project boards.
|
projects.desc = Manage issues and pulls in project boards.
|
||||||
|
@ -1619,9 +1618,9 @@ issues.no_content = No description provided.
|
||||||
issues.close = Close issue
|
issues.close = Close issue
|
||||||
issues.comment_pull_merged_at = merged commit %[1]s into %[2]s %[3]s
|
issues.comment_pull_merged_at = merged commit %[1]s into %[2]s %[3]s
|
||||||
issues.comment_manually_pull_merged_at = manually merged commit %[1]s into %[2]s %[3]s
|
issues.comment_manually_pull_merged_at = manually merged commit %[1]s into %[2]s %[3]s
|
||||||
issues.close_comment_issue = Comment and close
|
issues.close_comment_issue = Close with comment
|
||||||
issues.reopen_issue = Reopen
|
issues.reopen_issue = Reopen
|
||||||
issues.reopen_comment_issue = Comment and reopen
|
issues.reopen_comment_issue = Reopen with comment
|
||||||
issues.create_comment = Comment
|
issues.create_comment = Comment
|
||||||
issues.closed_at = `closed this issue <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
issues.closed_at = `closed this issue <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||||
issues.reopened_at = `reopened this issue <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
issues.reopened_at = `reopened this issue <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||||
|
@ -2025,8 +2024,7 @@ signing.wont_sign.commitssigned = The merge will not be signed as all the associ
|
||||||
signing.wont_sign.approved = The merge will not be signed as the PR is not approved.
|
signing.wont_sign.approved = The merge will not be signed as the PR is not approved.
|
||||||
signing.wont_sign.not_signed_in = You are not signed in.
|
signing.wont_sign.not_signed_in = You are not signed in.
|
||||||
|
|
||||||
ext_wiki = Access to external Wiki
|
ext_wiki = External Wiki
|
||||||
ext_wiki.desc = Link to an external wiki.
|
|
||||||
|
|
||||||
wiki = Wiki
|
wiki = Wiki
|
||||||
wiki.welcome = Welcome to the Wiki.
|
wiki.welcome = Welcome to the Wiki.
|
||||||
|
@ -2766,6 +2764,26 @@ error.csv.unexpected = Can't render this file because it contains an unexpected
|
||||||
error.csv.invalid_field_count = Can't render this file because it has a wrong number of fields in line %d.
|
error.csv.invalid_field_count = Can't render this file because it has a wrong number of fields in line %d.
|
||||||
error.broken_git_hook = Git hooks of this repository seem to be broken. Please follow the <a target="_blank" rel="noreferrer" href="%s">documentation</a> to fix them, then push some commits to refresh the status.
|
error.broken_git_hook = Git hooks of this repository seem to be broken. Please follow the <a target="_blank" rel="noreferrer" href="%s">documentation</a> to fix them, then push some commits to refresh the status.
|
||||||
|
|
||||||
|
[repo.permissions]
|
||||||
|
code.read = <b>Read:</b> Access and clone the code of the repository.
|
||||||
|
code.write = <b>Write:</b> Push to the repository, create branches and tags.
|
||||||
|
issues.read = <b>Read:</b> Read and create issues and comments.
|
||||||
|
issues.write = <b>Write:</b> Close issues and manage metadata like labels, milestones, assignees, due dates and dependencies.
|
||||||
|
pulls.read = <b>Read:</b> Reading and create pull requests.
|
||||||
|
pulls.write = <b>Write:</b> Close pull requests and manage metadata like labels, milestones, assignees, due dates and dependencies.
|
||||||
|
releases.read = <b>Read:</b> View and download releases.
|
||||||
|
releases.write = <b>Write:</b> Publish, edit and delete releases and their assets.
|
||||||
|
wiki.read = <b>Read:</b> Read the integrated wiki and it's history.
|
||||||
|
wiki.write = <b>Write:</b> Create, update and delete pages in the integrated wiki.
|
||||||
|
projects.read = <b>Read:</b> Access repository project boards.
|
||||||
|
projects.write = <b>Write:</b> Create projects and columns and edit them.
|
||||||
|
packages.read = <b>Read:</b> View and download packages assigned to the repository.
|
||||||
|
packages.write = <b>Write:</b> Publish and delete packages assigned to the repository.
|
||||||
|
actions.read = <b>Read:</b> View integrated CI/CD pipelines and their logs.
|
||||||
|
actions.write = <b>Write:</b> Manually trigger, restart, cancel or approve pending CI/CD pipelines.
|
||||||
|
ext_issues = Access the link to an external issue tracker. The permissions are managed externally.
|
||||||
|
ext_wiki = Access the link to an external wiki. The permissions are managed externally.
|
||||||
|
|
||||||
[graphs]
|
[graphs]
|
||||||
component_loading = Loading %s...
|
component_loading = Loading %s...
|
||||||
component_loading_failed = Could not load %s
|
component_loading_failed = Could not load %s
|
||||||
|
@ -3733,7 +3751,7 @@ management = Manage secrets
|
||||||
|
|
||||||
[actions]
|
[actions]
|
||||||
actions = Actions
|
actions = Actions
|
||||||
unit.desc = Manage integrated CI/CD pipelines with Forgejo Actions
|
unit.desc = Manage integrated CI/CD pipelines with Forgejo Actions.
|
||||||
|
|
||||||
status.unknown = Unknown
|
status.unknown = Unknown
|
||||||
status.waiting = Waiting
|
status.waiting = Waiting
|
||||||
|
|
|
@ -65,8 +65,8 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ctx.Locale.Tr "units.unit"}}</th>
|
<th>{{ctx.Locale.Tr "units.unit"}}</th>
|
||||||
<th id="access_none">{{ctx.Locale.Tr "org.teams.none_access"}}</th>
|
<th id="access_none">{{ctx.Locale.Tr "org.teams.none_access"}}</th>
|
||||||
<th id="access_read">{{ctx.Locale.Tr "org.teams.read_access"}}</th>
|
<th>{{ctx.Locale.Tr "org.teams.read_access"}}</th>
|
||||||
<th id="access_write">{{ctx.Locale.Tr "org.teams.write_access"}}</th>
|
<th>{{ctx.Locale.Tr "org.teams.write_access"}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -75,25 +75,26 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<label {{if $unit.Type.UnitGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
|
<label {{if $unit.Type.UnitGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
|
||||||
{{ctx.Locale.Tr $unit.NameKey}}{{if $unit.Type.UnitGlobalDisabled}} {{ctx.Locale.Tr "org.team_unit_disabled"}}{{end}}
|
<span id="help_{{$unit.Type.Value}}_name">{{ctx.Locale.Tr $unit.NameKey}}{{if $unit.Type.UnitGlobalDisabled}} {{ctx.Locale.Tr "org.team_unit_disabled"}}{{end}}</span>
|
||||||
<span class="help">{{ctx.Locale.Tr $unit.DescKey}}</span>
|
<span class="help" id="help_{{$unit.Type.Value}}_r">{{ctx.Locale.Tr (print "repo.permissions." $unit.Name ".read")}}</span>
|
||||||
|
<span class="help" id="help_{{$unit.Type.Value}}_w">{{ctx.Locale.Tr (print "repo.permissions." $unit.Name ".write")}}</span>
|
||||||
</label>
|
</label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<label>
|
<label>
|
||||||
<input aria-labelledby="access_none" type="radio" name="unit_{{$unit.Type.Value}}" value="0"{{if or ($unit.Type.UnitGlobalDisabled) (eq ($.Team.UnitAccessMode $.Context $unit.Type) 0)}} checked{{end}}>
|
<input aria-labelledby="help_{{$unit.Type.Value}}_name access_none" type="radio" name="unit_{{$unit.Type.Value}}" value="0"{{if or ($unit.Type.UnitGlobalDisabled) (eq ($.Team.UnitAccessMode $.Context $unit.Type) 0)}} checked{{end}}>
|
||||||
<span class="only-mobile">{{ctx.Locale.Tr "org.teams.none_access"}}</span>
|
<span class="only-mobile">{{ctx.Locale.Tr "org.teams.none_access"}}</span>
|
||||||
</label>
|
</label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<label>
|
<label>
|
||||||
<input aria-labelledby="access_read" type="radio" name="unit_{{$unit.Type.Value}}" value="1"{{if or (eq $.Team.ID 0) (eq ($.Team.UnitAccessMode $.Context $unit.Type) 1)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}}>
|
<input aria-labelledby="help_{{$unit.Type.Value}}_name help_{{$unit.Type.Value}}_r" type="radio" name="unit_{{$unit.Type.Value}}" value="1"{{if or (eq $.Team.ID 0) (eq ($.Team.UnitAccessMode $.Context $unit.Type) 1)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}}>
|
||||||
<span class="only-mobile">{{ctx.Locale.Tr "org.teams.read_access"}}</span>
|
<span class="only-mobile">{{ctx.Locale.Tr "org.teams.read_access"}}</span>
|
||||||
</label>
|
</label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<label>
|
<label>
|
||||||
<input aria-labelledby="access_write" type="radio" name="unit_{{$unit.Type.Value}}" value="2"{{if (ge ($.Team.UnitAccessMode $.Context $unit.Type) 2)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}}>
|
<input aria-labelledby="help_{{$unit.Type.Value}}_name help_{{$unit.Type.Value}}_w" type="radio" name="unit_{{$unit.Type.Value}}" value="2"{{if (ge ($.Team.UnitAccessMode $.Context $unit.Type) 2)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}}>
|
||||||
<span class="only-mobile">{{ctx.Locale.Tr "org.teams.write_access"}}</span>
|
<span class="only-mobile">{{ctx.Locale.Tr "org.teams.write_access"}}</span>
|
||||||
</label>
|
</label>
|
||||||
</td>
|
</td>
|
||||||
|
@ -108,7 +109,7 @@
|
||||||
<label {{if $unit.Type.UnitGlobalDisabled}}data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
|
<label {{if $unit.Type.UnitGlobalDisabled}}data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
|
||||||
<input type="checkbox" name="unit_{{$unit.Type.Value}}" value="1"{{if or (eq $.Team.ID 0) (eq ($.Team.UnitAccessMode $.Context $unit.Type) 1)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}}>
|
<input type="checkbox" name="unit_{{$unit.Type.Value}}" value="1"{{if or (eq $.Team.ID 0) (eq ($.Team.UnitAccessMode $.Context $unit.Type) 1)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}}>
|
||||||
{{ctx.Locale.Tr $unit.NameKey}}{{if $unit.Type.UnitGlobalDisabled}} {{ctx.Locale.Tr "org.team_unit_disabled"}}{{end}}
|
{{ctx.Locale.Tr $unit.NameKey}}{{if $unit.Type.UnitGlobalDisabled}} {{ctx.Locale.Tr "org.team_unit_disabled"}}{{end}}
|
||||||
<span class="help">{{ctx.Locale.Tr $unit.DescKey}}</span>
|
<span class="help">{{ctx.Locale.Tr (print "repo.permissions." $unit.Name)}}</span>
|
||||||
</label>
|
</label>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -21,10 +21,24 @@ export async function validate_form({page}, scope) {
|
||||||
await expect(b).toHaveCSS('margin-top', '0px');
|
await expect(b).toHaveCSS('margin-top', '0px');
|
||||||
await expect(b).toHaveCSS('vertical-align', 'baseline');
|
await expect(b).toHaveCSS('vertical-align', 'baseline');
|
||||||
}
|
}
|
||||||
|
|
||||||
// assert no (trailing) colon is used in labels
|
// assert no (trailing) colon is used in labels
|
||||||
// might be necessary to adjust in case colons are strictly necessary in help text
|
// might be necessary to adjust in case colons are strictly necessary in help text
|
||||||
for (const l of await page.locator('label').all()) {
|
for (const l of await page.locator('label').all()) {
|
||||||
const str = await l.textContent();
|
const str = await l.textContent();
|
||||||
await expect(str.split('\n')[0]).not.toContain(':');
|
await expect(str.split('\n')[0]).not.toContain(':');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check that multiple help text are correctly aligned to each other
|
||||||
|
// used for example to separate read/write permissions in team permission matrix
|
||||||
|
for (const l of await page.locator('label:has(.help + .help)').all()) {
|
||||||
|
const helpLabels = await l.locator('.help').all();
|
||||||
|
const boxes = await Promise.all(helpLabels.map((help) => help.boundingBox()));
|
||||||
|
for (let i = 1; i < boxes.length; i++) {
|
||||||
|
// help texts vertically aligned on top of each other
|
||||||
|
await expect(boxes[i].x).toBe(boxes[0].x);
|
||||||
|
// help texts don't horizontally intersect each other
|
||||||
|
await expect(boxes[i].y + boxes[i].height).toBeGreaterThanOrEqual(boxes[i - 1].y + boxes[i - 1].height);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue